From 84224469eb1fa5b64b7a89bd676b06a3eaed4048 Mon Sep 17 00:00:00 2001 From: Rolson Quadras Date: Wed, 14 Feb 2024 10:56:40 -0500 Subject: [PATCH] refactor: remove sidetreelongform vdr impl The sidetreelongform vdr impl was moved to sidetree-go repo (https://github.com/trustbloc/sidetree-go/pull/9) to remove did-go dependecy on sidetree-go repo. Signed-off-by: Rolson Quadras --- .golangci.yml | 2 +- go.mod | 10 +- go.sum | 18 - method/jwk/creator.go | 2 +- method/jwk/creator_test.go | 2 +- method/jwk/resolver_test.go | 2 +- method/sidetreelongform/README.md | 107 -- .../sidetreelongform/dochandler/dochandler.go | 234 ----- .../dochandler/dochandler_test.go | 285 ------ .../protocol/nsprovider/namespaceprovider.go | 55 - .../nsprovider/namespaceprovider_test.go | 62 -- .../protocol/verprovider/versionprovider.go | 84 -- .../verprovider/versionprovider_test.go | 125 --- .../clientregistry/clientregistry.go | 71 -- .../clientregistry/clientregistry_test.go | 40 - .../clientregistry/mocks/clientfactory.gen.go | 117 --- .../clientregistry/testversions.go | 29 - .../clientregistry/versions.go | 22 - .../protocolversion/common/version.go | 71 -- .../protocolversion/common/version_test.go | 41 - .../versions/common/protocol.go | 57 -- .../versions/common/protocol_test.go | 35 - .../versions/v1_0/client/client.go | 51 - .../versions/v1_0/client/client_test.go | 27 - .../versions/v1_0/config/protocol.go | 36 - .../versions/v1_0/config/protocol_test.go | 27 - method/sidetreelongform/sidetree/api/api.go | 21 - method/sidetreelongform/sidetree/client.go | 668 ------------ .../sidetreelongform/sidetree/client_test.go | 965 ------------------ method/sidetreelongform/sidetree/doc/doc.go | 199 ---- method/sidetreelongform/sidetree/option.go | 41 - .../sidetree/option/create/option.go | 87 -- .../sidetree/option/deactivate/option.go | 42 - .../sidetree/option/recovery/option.go | 102 -- .../sidetree/option/update/option.go | 110 -- method/sidetreelongform/vdr.go | 386 ------- method/sidetreelongform/vdr_test.go | 508 --------- pkg/canonicalizer/canonicalizer.go | 29 + pkg/canonicalizer/canonicalizer_test.go | 43 + pkg/internal/jsoncanonicalizer/README.md | 4 + pkg/internal/jsoncanonicalizer/es6numfmt.go | 94 ++ .../jsoncanonicalizer/jsoncanonicalizer.go | 379 +++++++ 42 files changed, 555 insertions(+), 4735 deletions(-) delete mode 100644 method/sidetreelongform/README.md delete mode 100644 method/sidetreelongform/dochandler/dochandler.go delete mode 100644 method/sidetreelongform/dochandler/dochandler_test.go delete mode 100644 method/sidetreelongform/dochandler/protocol/nsprovider/namespaceprovider.go delete mode 100644 method/sidetreelongform/dochandler/protocol/nsprovider/namespaceprovider_test.go delete mode 100644 method/sidetreelongform/dochandler/protocol/verprovider/versionprovider.go delete mode 100644 method/sidetreelongform/dochandler/protocol/verprovider/versionprovider_test.go delete mode 100644 method/sidetreelongform/dochandler/protocolversion/clientregistry/clientregistry.go delete mode 100644 method/sidetreelongform/dochandler/protocolversion/clientregistry/clientregistry_test.go delete mode 100644 method/sidetreelongform/dochandler/protocolversion/clientregistry/mocks/clientfactory.gen.go delete mode 100644 method/sidetreelongform/dochandler/protocolversion/clientregistry/testversions.go delete mode 100644 method/sidetreelongform/dochandler/protocolversion/clientregistry/versions.go delete mode 100644 method/sidetreelongform/dochandler/protocolversion/common/version.go delete mode 100644 method/sidetreelongform/dochandler/protocolversion/common/version_test.go delete mode 100644 method/sidetreelongform/dochandler/protocolversion/versions/common/protocol.go delete mode 100644 method/sidetreelongform/dochandler/protocolversion/versions/common/protocol_test.go delete mode 100644 method/sidetreelongform/dochandler/protocolversion/versions/v1_0/client/client.go delete mode 100644 method/sidetreelongform/dochandler/protocolversion/versions/v1_0/client/client_test.go delete mode 100644 method/sidetreelongform/dochandler/protocolversion/versions/v1_0/config/protocol.go delete mode 100644 method/sidetreelongform/dochandler/protocolversion/versions/v1_0/config/protocol_test.go delete mode 100644 method/sidetreelongform/sidetree/api/api.go delete mode 100644 method/sidetreelongform/sidetree/client.go delete mode 100644 method/sidetreelongform/sidetree/client_test.go delete mode 100644 method/sidetreelongform/sidetree/doc/doc.go delete mode 100644 method/sidetreelongform/sidetree/option.go delete mode 100644 method/sidetreelongform/sidetree/option/create/option.go delete mode 100644 method/sidetreelongform/sidetree/option/deactivate/option.go delete mode 100644 method/sidetreelongform/sidetree/option/recovery/option.go delete mode 100644 method/sidetreelongform/sidetree/option/update/option.go delete mode 100644 method/sidetreelongform/vdr.go delete mode 100644 method/sidetreelongform/vdr_test.go create mode 100644 pkg/canonicalizer/canonicalizer.go create mode 100644 pkg/canonicalizer/canonicalizer_test.go create mode 100644 pkg/internal/jsoncanonicalizer/README.md create mode 100644 pkg/internal/jsoncanonicalizer/es6numfmt.go create mode 100644 pkg/internal/jsoncanonicalizer/jsoncanonicalizer.go diff --git a/.golangci.yml b/.golangci.yml index d7cae30..f0cc564 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -11,7 +11,7 @@ run: tests: true build-tags: [""] skip-dirs: - - pkg/crypto/primitive/bbs12381g2pub/internal/kilic/bls12-381 + - pkg/internal/jsoncanonicalizer output: format: colored-line-number diff --git a/go.mod b/go.mod index e00ab18..38bd5c7 100644 --- a/go.mod +++ b/go.mod @@ -15,9 +15,7 @@ require ( github.com/multiformats/go-multibase v0.1.1 github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f github.com/stretchr/testify v1.8.2 - github.com/trustbloc/bbs-signature-go v1.0.2-0.20240117165819-e99610e107f4 github.com/trustbloc/kms-go v1.1.1-0.20240117181216-c38a74431167 - github.com/trustbloc/sidetree-go v1.0.1-0.20240117181443-2f0d5acb92b7 github.com/xeipuuv/gojsonschema v1.2.0 ) @@ -29,22 +27,18 @@ require ( github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect - github.com/evanphx/json-patch v4.1.0+incompatible // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 // indirect github.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69 // indirect - github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect - github.com/minio/sha256-simd v0.1.1 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.1.0 // indirect - github.com/multiformats/go-multihash v0.0.14 // indirect - github.com/multiformats/go-varint v0.0.6 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/cachecontrol v0.1.0 // indirect - github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8 // indirect + github.com/trustbloc/bbs-signature-go v1.0.2-0.20240117165819-e99610e107f4 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect golang.org/x/crypto v0.17.0 // indirect diff --git a/go.sum b/go.sum index 443b6df..95cef03 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/evanphx/json-patch v4.1.0+incompatible h1:K1MDoo4AZ4wU0GIU/fPmtZg7VpzLjCxu+UwBD1FvwOc= -github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= @@ -54,17 +52,11 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= -github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= -github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= @@ -73,11 +65,6 @@ github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ8 github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI= github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8= -github.com/multiformats/go-multihash v0.0.14 h1:QoBceQYQQtNUuf6s7wHxnE2c8bhbMqhfGzNI032se/I= -github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= -github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -91,8 +78,6 @@ github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8 github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -108,8 +93,6 @@ github.com/trustbloc/bbs-signature-go v1.0.2-0.20240117165819-e99610e107f4 h1:Yw github.com/trustbloc/bbs-signature-go v1.0.2-0.20240117165819-e99610e107f4/go.mod h1:xYotcXHAbcE0TO+SteW0J6XI3geQaXq4wdnXR2k+XCU= github.com/trustbloc/kms-go v1.1.1-0.20240117181216-c38a74431167 h1:tgzKqzUJRLg0sY/BmHEDpMQ3TZOtC7u1RDYEMPeqnCI= github.com/trustbloc/kms-go v1.1.1-0.20240117181216-c38a74431167/go.mod h1:23chV97aYVcT3ihO8xO9+T0KZn9lFGbk2KXhJNaHbXE= -github.com/trustbloc/sidetree-go v1.0.1-0.20240117181443-2f0d5acb92b7 h1:xx5Fg5boc9ibrltoaapZRTvti3BAqchzglUdrY1oZuY= -github.com/trustbloc/sidetree-go v1.0.1-0.20240117181443-2f0d5acb92b7/go.mod h1:cYiU0rOxzOoLldb0cKfVlWlBD5gk+q4MVgpkX+4jWT4= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -119,7 +102,6 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 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-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/method/jwk/creator.go b/method/jwk/creator.go index b475869..4e59d5e 100644 --- a/method/jwk/creator.go +++ b/method/jwk/creator.go @@ -10,8 +10,8 @@ import ( "encoding/base64" "fmt" + "github.com/trustbloc/did-go/pkg/canonicalizer" "github.com/trustbloc/kms-go/doc/jose/jwk" - "github.com/trustbloc/sidetree-go/pkg/canonicalizer" "github.com/trustbloc/did-go/doc/did" vdrapi "github.com/trustbloc/did-go/vdr/api" diff --git a/method/jwk/creator_test.go b/method/jwk/creator_test.go index 8b8ebbe..166a632 100644 --- a/method/jwk/creator_test.go +++ b/method/jwk/creator_test.go @@ -13,9 +13,9 @@ import ( "testing" "github.com/stretchr/testify/require" + "github.com/trustbloc/did-go/pkg/canonicalizer" jwkapi "github.com/trustbloc/kms-go/doc/jose/jwk" "github.com/trustbloc/kms-go/doc/jose/jwk/jwksupport" - "github.com/trustbloc/sidetree-go/pkg/canonicalizer" "github.com/trustbloc/did-go/doc/did" "github.com/trustbloc/did-go/method/jwk" diff --git a/method/jwk/resolver_test.go b/method/jwk/resolver_test.go index 07ef3c8..39c72f3 100644 --- a/method/jwk/resolver_test.go +++ b/method/jwk/resolver_test.go @@ -12,7 +12,7 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/trustbloc/sidetree-go/pkg/canonicalizer" + "github.com/trustbloc/did-go/pkg/canonicalizer" "github.com/trustbloc/did-go/doc/did" "github.com/trustbloc/did-go/method/jwk" diff --git a/method/sidetreelongform/README.md b/method/sidetreelongform/README.md deleted file mode 100644 index f1bae97..0000000 --- a/method/sidetreelongform/README.md +++ /dev/null @@ -1,107 +0,0 @@ -# Long Form VDR -Long form VDR is used to resolve long form DID and to create long-form DID. -Update, recover and deactivate operations are currently not supported. - -## New VDR -``` -import ( - "crypto" - "github.com/trustbloc/did-go/method/sidetreelongform" -) - -vdr, err := sidetreelongform.New() - if err != nil { - return err -} -``` - -## Create DID -For creating DID use vdr create and pass DID document. - -``` -import ( -"crypto" -"crypto/ed25519" -"crypto/rand" -"fmt" - -ariesdid "github.com/hyperledger/aries-framework-go/pkg/doc/did" -"github.com/hyperledger/aries-framework-go/pkg/doc/jose" -vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr" - -"github.com/trustbloc/did-go/method/sidetreelongform" -) - -recoveryKey, recoveryKeyPrivateKey, err := ed25519.GenerateKey(rand.Reader) -if err != nil { - return err -} - -updateKey, updateKeyPrivateKey, err := ed25519.GenerateKey(rand.Reader) -if err != nil { - return err -} - -didPublicKey, _, err := ed25519.GenerateKey(rand.Reader) -if err != nil { - return err -} - -jwk, err := jose.JWKFromKey(didPublicKey) -if err != nil { - return err -} - -vm,err:=ariesdid.NewVerificationMethodFromJWK("key1", "Ed25519VerificationKey2018", "", jwk) -if err != nil { - return err -} - -didDoc := &ariesdid.Doc{} - -// add did keys -didDoc.Authentication = append(didDoc.Authentication, *ariesdid.NewReferencedVerification(vm, - ariesdid.Authentication)) - -// add did services -didDoc.Service = []ariesdid.Service{{ID: "svc1", Type: "type", ServiceEndpoint: "http://www.example.com/"}} - -// create did -createdDocResolution, err := vdr.Create(didDoc, - vdrapi.WithOption(sidetreelongform.RecoveryPublicKeyOpt, recoveryKey), - vdrapi.WithOption(sidetreelongform.UpdatePublicKeyOpt, updateKey), -if err != nil { - return err -} - -fmt.Println(createdDocResolution.DIDDocument.ID) - -// recovery private key should be saved for future use. -keyRetrieverImpl.recoverKey = recoveryKeyPrivateKey -// update private key should be saved for future use. -keyRetrieverImpl.updateKey = updateKeyPrivateKey - - -sidetreelongformDID := createdDocResolution.DIDDocument.ID -``` - -## Resolve DID -For resolving DID use vdr read and pass long form DID. - -``` -docResolution, err := vdr.Read(sidetreelongformDID) -if err != nil { - return err -} - -fmt.Println(docResolution.DIDDocument.ID) -``` - -## Update DID -Not supported. - -## Recover DID -Not supported. - -## Deactivate DID -Not supported. diff --git a/method/sidetreelongform/dochandler/dochandler.go b/method/sidetreelongform/dochandler/dochandler.go deleted file mode 100644 index cfc796b..0000000 --- a/method/sidetreelongform/dochandler/dochandler.go +++ /dev/null @@ -1,234 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -// Package dochandler performs document operation processing and document resolution. -// -// the supplied create request is used directly to generate and return a resolved document. -// In this case the supplied create request is subject to the same validation as in a create operation. -package dochandler - -import ( - "fmt" - "strings" - - "github.com/trustbloc/sidetree-go/pkg/api/operation" - "github.com/trustbloc/sidetree-go/pkg/api/protocol" - "github.com/trustbloc/sidetree-go/pkg/canonicalizer" - "github.com/trustbloc/sidetree-go/pkg/document" - "github.com/trustbloc/sidetree-go/pkg/docutil" - "github.com/trustbloc/sidetree-go/pkg/encoder" - - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocol/nsprovider" - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocol/verprovider" - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/clientregistry" - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/versions/common" -) - -const ( - badRequest = "bad request" - v1 = "1.0" -) - -// DocumentHandler implements document handler. -type DocumentHandler struct { - protocolClient protocol.Client - protocolVersions []string - currentProtocolVersion string - - namespace string -} - -// Option is an option for document handler. -type Option func(opts *DocumentHandler) - -// WithProtocolVersions sets optional client protocol versions. -func WithProtocolVersions(versions []string) Option { - return func(opts *DocumentHandler) { - opts.protocolVersions = versions - } -} - -// WithCurrentProtocolVersion sets optional current protocol versions. -// Defaults to the latest in the protocol versions list. -func WithCurrentProtocolVersion(version string) Option { - return func(opts *DocumentHandler) { - opts.currentProtocolVersion = version - } -} - -// New creates a new document handler with the context. -func New(namespace string, opts ...Option) (*DocumentHandler, error) { - dh := &DocumentHandler{ - namespace: namespace, - protocolVersions: []string{v1}, - currentProtocolVersion: v1, - } - - // apply options - for _, opt := range opts { - opt(dh) - } - - pc, err := createProtocolClient(dh.namespace, dh.protocolVersions, dh.currentProtocolVersion) - if err != nil { - return nil, err - } - - dh.protocolClient = pc - - return dh, nil -} - -// Namespace returns the namespace of the document handler. -func (r *DocumentHandler) Namespace() string { - return r.namespace -} - -// ProcessOperation validates create operation and returns resolution result of that create operation. -// Only create operation is supported. -func (r *DocumentHandler) ProcessOperation(operationBuffer []byte) (*document.ResolutionResult, error) { - pv, err := r.protocolClient.Current() - if err != nil { - return nil, err - } - - op, err := pv.OperationParser().Parse(r.namespace, operationBuffer) - if err != nil { - return nil, fmt.Errorf("%s: %s", badRequest, err.Error()) - } - - if op.Type != operation.TypeCreate { - return nil, fmt.Errorf("%s: %s", badRequest, err.Error()) - } - - jcsBytes, err := canonicalizer.MarshalCanonical(operationBuffer) - if err != nil { - return nil, fmt.Errorf("%s: %s", badRequest, err.Error()) - } - - requestJCS := encoder.EncodeToString(jcsBytes) - - ti := docutil.GetTransformationInfoForUnpublished(r.namespace, "", "", op.UniqueSuffix, requestJCS) - - return r.getCreateResponse(op, ti, pv) -} - -func (r *DocumentHandler) getCreateResponse(op *operation.Operation, - ti protocol.TransformationInfo, pv protocol.Version) (*document.ResolutionResult, error) { - rm, err := docutil.GetCreateResult(op, pv) - if err != nil { - return nil, err - } - - return pv.DocumentTransformer().TransformDocument(rm, ti) -} - -// ResolveDocument resolves long form DID format. -// -// Long Form DID format: -// did:METHOD::Base64url(JCS({suffix-data-object, delta-object})) -// -// The and are used to generate and return resolved DID Document. -// In this case the supplied delta and suffix objects are subject to the same validation -// as during processing create operation. -func (r *DocumentHandler) ResolveDocument(longFormDID string, - _ ...document.ResolutionOption) (*document.ResolutionResult, error) { - ns, err := r.getNamespace(longFormDID) - if err != nil { - return nil, fmt.Errorf("%s: %s", badRequest, err.Error()) - } - - pv, err := r.protocolClient.Current() - if err != nil { - return nil, err - } - - // extract did and initial document value - shortFormDID, createReq, err := pv.OperationParser().ParseDID(ns, longFormDID) - if err != nil { - return nil, fmt.Errorf("%s: %s", badRequest, err.Error()) - } - - if createReq == nil { - return nil, fmt.Errorf("%s: %s", badRequest, "missing create request") - } - - uniquePortion, err := getSuffix(shortFormDID) - if err != nil { - return nil, fmt.Errorf("%s: %s", badRequest, err.Error()) - } - - return r.resolveRequestWithInitialState(uniquePortion, longFormDID, createReq, pv) -} - -func (r *DocumentHandler) getNamespace(shortOrLongFormDID string) (string, error) { - if strings.HasPrefix(shortOrLongFormDID, r.namespace) { - return r.namespace, nil - } - - return "", fmt.Errorf("did must start with configured namespace[%s]", r.namespace) -} - -func (r *DocumentHandler) resolveRequestWithInitialState(uniqueSuffix, longFormDID string, initialBytes []byte, - pv protocol.Version) (*document.ResolutionResult, error) { - op, err := pv.OperationParser().Parse(r.namespace, initialBytes) - if err != nil { - return nil, fmt.Errorf("%s: %s", badRequest, err.Error()) - } - - if uniqueSuffix != op.UniqueSuffix { - return nil, fmt.Errorf("%s: provided did doesn't match did created from initial state", badRequest) - } - - createRequestJCS := longFormDID[strings.LastIndex(longFormDID, docutil.NamespaceDelimiter)+1:] - ti := docutil.GetTransformationInfoForUnpublished(r.namespace, "", "", uniqueSuffix, createRequestJCS) - - return r.getCreateResponse(op, ti, pv) -} - -// getSuffix returns suffix from short form DID. -func getSuffix(shortFormDID string) (string, error) { - parts := strings.Split(shortFormDID, docutil.NamespaceDelimiter) - - const minParts = 3 - if len(parts) < minParts { - return "", fmt.Errorf("invalid number of parts[%d] for DID identifier", len(parts)) - } - - // suffix is always the last part - suffix := parts[len(parts)-1] - - return suffix, nil -} - -func createProtocolClient(namespace string, - versions []string, currentVersion string, -) (nsprovider.ClientVersionProvider, error) { - registry := clientregistry.New() - - var clientVersions []protocol.Version - - config := &common.ProtocolConfig{EnableBase: true} - - for _, version := range versions { - cv, err := registry.CreateClientVersion(version, config) - if err != nil { - return nil, fmt.Errorf("error creating client version [%s]: %w", version, err) - } - - clientVersions = append(clientVersions, cv) - } - - verProvider, err := verprovider.New(clientVersions, verprovider.WithCurrentProtocolVersion(currentVersion)) - if err != nil { - return nil, fmt.Errorf("failed to create version provider: %w", err) - } - - nsProvider := nsprovider.New() - nsProvider.Add(namespace, verProvider) - - return nsProvider.ForNamespace(namespace) -} diff --git a/method/sidetreelongform/dochandler/dochandler_test.go b/method/sidetreelongform/dochandler/dochandler_test.go deleted file mode 100644 index 6f8c834..0000000 --- a/method/sidetreelongform/dochandler/dochandler_test.go +++ /dev/null @@ -1,285 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ -package dochandler - -import ( - "encoding/json" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "github.com/trustbloc/sidetree-go/pkg/document" - "github.com/trustbloc/sidetree-go/pkg/encoder" - "github.com/trustbloc/sidetree-go/pkg/mocks" - "github.com/trustbloc/sidetree-go/pkg/versions/1_0/model" -) - -const ( - namespace = "did:ion" -) - -func TestNew(t *testing.T) { - t.Run("success", func(t *testing.T) { - lfh, err := New(namespace) - require.NoError(t, err) - - require.Equal(t, namespace, lfh.Namespace()) - }) - - t.Run("success - with protocol versions", func(t *testing.T) { - lfh, err := New(namespace, - WithProtocolVersions([]string{v1}), - WithCurrentProtocolVersion(v1)) - require.NoError(t, err) - require.NotNil(t, lfh) - }) - - t.Run("error - without protocol versions", func(t *testing.T) { - lfh, err := New(namespace, - WithProtocolVersions([]string{})) - require.Nil(t, lfh) - require.Error(t, err) - require.Contains(t, err.Error(), - "failed to create version provider: must provide at least one client version") - }) -} - -func TestResolve(t *testing.T) { - longFormDID := fmt.Sprintf("%s:%s:%s", namespace, didSuffix, requestJCS) - - t.Run("success", func(t *testing.T) { - lfh, err := New(namespace) - require.NoError(t, err) - - doc, err := lfh.ResolveDocument(longFormDID) - require.NoError(t, err) - require.NotNil(t, doc) - - docBytes, err := json.Marshal(doc.Document) - require.NoError(t, err) - - err = prettyPrint(doc.Document) - require.NoError(t, err) - - var didDoc document.DIDDocument - err = json.Unmarshal(docBytes, &didDoc) - require.NoError(t, err) - - var expectedIONDoc document.DIDDocument - err = json.Unmarshal([]byte(ionDIDDoc), &expectedIONDoc) - require.NoError(t, err) - - require.Equal(t, doc.Document.ID(), expectedIONDoc.ID()) - require.Equal(t, len(expectedIONDoc.Services()), len(didDoc.Services())) - require.Equal(t, len(expectedIONDoc.PublicKeys()), len(didDoc.PublicKeys())) - require.Equal(t, didDoc[document.KeyPurposeAssertionMethod], expectedIONDoc[document.KeyPurposeAssertionMethod]) - require.Equal(t, didDoc[document.KeyPurposeAuthentication], expectedIONDoc[document.KeyPurposeAuthentication]) - require.Equal(t, - didDoc[document.KeyPurposeCapabilityDelegation], - expectedIONDoc[document.KeyPurposeCapabilityDelegation]) - require.Equal(t, - didDoc[document.KeyPurposeCapabilityInvocation], - expectedIONDoc[document.KeyPurposeCapabilityInvocation]) - require.Equal(t, didDoc[document.KeyPurposeKeyAgreement], expectedIONDoc[document.KeyPurposeKeyAgreement]) - - require.Equal(t, - didDoc.VerificationMethods()[0].Controller(), - expectedIONDoc.VerificationMethods()[0].Controller()) - require.Equal(t, didDoc.VerificationMethods()[0].ID(), expectedIONDoc.VerificationMethods()[0].ID()) - require.Equal(t, - didDoc.VerificationMethods()[0].PublicKeyJwk(), - expectedIONDoc.VerificationMethods()[0].PublicKeyJwk()) - - require.Equal(t, didDoc.Context()[0], expectedIONDoc.Context()[0]) - require.Equal(t, didDoc.Context()[1], expectedIONDoc.Context()[1]) - }) - - t.Run("success - with protocol versions", func(t *testing.T) { - lfh, err := New(namespace, - WithProtocolVersions([]string{v1}), - WithCurrentProtocolVersion(v1)) - require.NoError(t, err) - - doc, err := lfh.ResolveDocument(longFormDID) - require.NoError(t, err) - require.NotNil(t, doc) - }) - - t.Run("error - protocol client error", func(t *testing.T) { - lfh, err := New(namespace) - require.NoError(t, err) - - mockProtocolClient := mocks.NewMockProtocolClient() - mockProtocolClient.Err = fmt.Errorf("protocol client error") - - lfh.protocolClient = mockProtocolClient - - doc, err := lfh.ResolveDocument(longFormDID) - require.Error(t, err) - require.Nil(t, doc) - require.Contains(t, err.Error(), "protocol client error") - }) - - t.Run("error - parse operation fails due to invalid request JCS", func(t *testing.T) { - lfh, err := New(namespace) - require.NoError(t, err) - - // create invalid request JCS - reqBytes, err := json.Marshal(&model.CreateRequest{}) - require.NoError(t, err) - - invalidRequestJCS := encoder.EncodeToString(reqBytes) - - longFormDIDWithInvalidJCS := fmt.Sprintf("%s:%s:%s", namespace, didSuffix, invalidRequestJCS) - - doc, err := lfh.ResolveDocument(longFormDIDWithInvalidJCS) - require.Error(t, err) - require.Nil(t, doc) - require.Contains(t, err.Error(), "bad request: missing suffix data") - }) - - t.Run("error - wrong namespace", func(t *testing.T) { - lfh, err := New(namespace) - require.NoError(t, err) - - didWithInvalidNamespace := fmt.Sprintf("did:whatever:%s:%s", didSuffix, requestJCS) - - doc, err := lfh.ResolveDocument(didWithInvalidNamespace) - require.Error(t, err) - require.Nil(t, doc) - require.Contains(t, err.Error(), "bad request: did must start with configured namespace[did:ion]") - }) - - t.Run("error - missing create request", func(t *testing.T) { - lfh, err := New(namespace) - require.NoError(t, err) - - didWithoutRequestJCS := fmt.Sprintf("%s:%s", namespace, didSuffix) - - doc, err := lfh.ResolveDocument(didWithoutRequestJCS) - require.Error(t, err) - require.Nil(t, doc) - require.Contains(t, err.Error(), "bad request: missing create request") - }) - - t.Run("error - DID mismatch", func(t *testing.T) { - lfh, err := New(namespace) - require.NoError(t, err) - - longFormDIDWithInvalidSuffix := fmt.Sprintf("%s:%s:%s", namespace, "invalid", requestJCS) - - doc, err := lfh.ResolveDocument(longFormDIDWithInvalidSuffix) - require.Error(t, err) - require.Nil(t, doc) - require.Contains(t, err.Error(), "bad request: provided did doesn't match did created from initial state") - }) -} - -func TestProcess(t *testing.T) { - t.Run("success - ion request", func(t *testing.T) { - lfh, err := New(namespace) - require.NoError(t, err) - - opBytes, err := encoder.DecodeString(requestJCS) - require.NoError(t, err) - - doc, err := lfh.ProcessOperation(opBytes) - require.NoError(t, err) - require.NotNil(t, doc) - - err = prettyPrint(doc) - require.NoError(t, err) - - require.Equal(t, fmt.Sprintf("%s:%s:%s", namespace, didSuffix, requestJCS), doc.Document.ID()) - }) - - t.Run("success - local did", func(t *testing.T) { - lfh, err := New(namespace) - require.NoError(t, err) - - doc, err := lfh.ProcessOperation([]byte(request)) - require.NoError(t, err) - require.NotNil(t, doc) - - resolutionResult, err := lfh.ResolveDocument(doc.Document.ID()) - require.NoError(t, err) - require.NotNil(t, resolutionResult) - }) - - t.Run("error - invalid create request", func(t *testing.T) { - lfh, err := New(namespace) - require.NoError(t, err) - - // create invalid create request - reqBytes, err := json.Marshal(&model.CreateRequest{}) - require.NoError(t, err) - - doc, err := lfh.ProcessOperation(reqBytes) - require.Error(t, err) - require.Nil(t, doc) - require.Contains(t, err.Error(), "bad request: parse operation: operation type [] not supported") - }) -} - -func prettyPrint(result interface{}) error { - b, err := json.MarshalIndent(result, "", " ") - if err != nil { - return err - } - - fmt.Println(string(b)) - - return nil -} - -const didSuffix = `EiD9H4OHw5X4ctS1Q1G9LxmyEed9WDBW_QZ4VMpuOtRciw` - -//nolint:lll -const requestJCS = `eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJzaWduaW5nS2V5IiwicHVibGljS2V5SndrIjp7ImNydiI6InNlY3AyNTZrMSIsImt0eSI6IkVDIiwieCI6IndkRGZEakwxRlFET3NwcC1xdmRLUUtyNzllbTdOczJFNVNBVWE5aElRaTQiLCJ5IjoiUGZmc0hEYXA1X0t3UlZwNzgtaUJaQm5XQTZMS3p6bGIxSXJ3VWhFakpuOCJ9LCJwdXJwb3NlcyI6WyJhdXRoZW50aWNhdGlvbiIsImFzc2VydGlvbk1ldGhvZCIsImNhcGFiaWxpdHlJbnZvY2F0aW9uIiwiY2FwYWJpbGl0eURlbGVnYXRpb24iLCJrZXlBZ3JlZW1lbnQiXSwidHlwZSI6IkVjZHNhU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOSJ9XSwic2VydmljZXMiOltdfX1dLCJ1cGRhdGVDb21taXRtZW50IjoiRWlBazRmbkFKSTJuZ1Z5ZjhrZ05fbUI5emhmX2FKcmdwa2tlalVIbTR1X3gzQSJ9LCJzdWZmaXhEYXRhIjp7ImRlbHRhSGFzaCI6IkVpQVRaWi1jclh5OXFYeGhGdkFFZElhU0pLY0tTWTVubkZ5bkJCSWtsODF5N1EiLCJyZWNvdmVyeUNvbW1pdG1lbnQiOiJFaUFOOHQ3UHlZYmtONFc3ZEVZX1JZX25YWUNlc1JPQl9mUWxzdWx3eVNyYVF3In0sInR5cGUiOiJjcmVhdGUifQ` - -//nolint:lll -const ionDIDDoc = ` -{ - "id": "did:ion:EiD9H4OHw5X4ctS1Q1G9LxmyEed9WDBW_QZ4VMpuOtRciw:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJzaWduaW5nS2V5IiwicHVibGljS2V5SndrIjp7ImNydiI6InNlY3AyNTZrMSIsImt0eSI6IkVDIiwieCI6IndkRGZEakwxRlFET3NwcC1xdmRLUUtyNzllbTdOczJFNVNBVWE5aElRaTQiLCJ5IjoiUGZmc0hEYXA1X0t3UlZwNzgtaUJaQm5XQTZMS3p6bGIxSXJ3VWhFakpuOCJ9LCJwdXJwb3NlcyI6WyJhdXRoZW50aWNhdGlvbiIsImFzc2VydGlvbk1ldGhvZCIsImNhcGFiaWxpdHlJbnZvY2F0aW9uIiwiY2FwYWJpbGl0eURlbGVnYXRpb24iLCJrZXlBZ3JlZW1lbnQiXSwidHlwZSI6IkVjZHNhU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOSJ9XSwic2VydmljZXMiOltdfX1dLCJ1cGRhdGVDb21taXRtZW50IjoiRWlBazRmbkFKSTJuZ1Z5ZjhrZ05fbUI5emhmX2FKcmdwa2tlalVIbTR1X3gzQSJ9LCJzdWZmaXhEYXRhIjp7ImRlbHRhSGFzaCI6IkVpQVRaWi1jclh5OXFYeGhGdkFFZElhU0pLY0tTWTVubkZ5bkJCSWtsODF5N1EiLCJyZWNvdmVyeUNvbW1pdG1lbnQiOiJFaUFOOHQ3UHlZYmtONFc3ZEVZX1JZX25YWUNlc1JPQl9mUWxzdWx3eVNyYVF3In0sInR5cGUiOiJjcmVhdGUifQ", - "@context": [ - "https://www.w3.org/ns/did/v1", - { - "@base": "did:ion:EiD9H4OHw5X4ctS1Q1G9LxmyEed9WDBW_QZ4VMpuOtRciw:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJzaWduaW5nS2V5IiwicHVibGljS2V5SndrIjp7ImNydiI6InNlY3AyNTZrMSIsImt0eSI6IkVDIiwieCI6IndkRGZEakwxRlFET3NwcC1xdmRLUUtyNzllbTdOczJFNVNBVWE5aElRaTQiLCJ5IjoiUGZmc0hEYXA1X0t3UlZwNzgtaUJaQm5XQTZMS3p6bGIxSXJ3VWhFakpuOCJ9LCJwdXJwb3NlcyI6WyJhdXRoZW50aWNhdGlvbiIsImFzc2VydGlvbk1ldGhvZCIsImNhcGFiaWxpdHlJbnZvY2F0aW9uIiwiY2FwYWJpbGl0eURlbGVnYXRpb24iLCJrZXlBZ3JlZW1lbnQiXSwidHlwZSI6IkVjZHNhU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOSJ9XSwic2VydmljZXMiOltdfX1dLCJ1cGRhdGVDb21taXRtZW50IjoiRWlBazRmbkFKSTJuZ1Z5ZjhrZ05fbUI5emhmX2FKcmdwa2tlalVIbTR1X3gzQSJ9LCJzdWZmaXhEYXRhIjp7ImRlbHRhSGFzaCI6IkVpQVRaWi1jclh5OXFYeGhGdkFFZElhU0pLY0tTWTVubkZ5bkJCSWtsODF5N1EiLCJyZWNvdmVyeUNvbW1pdG1lbnQiOiJFaUFOOHQ3UHlZYmtONFc3ZEVZX1JZX25YWUNlc1JPQl9mUWxzdWx3eVNyYVF3In0sInR5cGUiOiJjcmVhdGUifQ" - } - ], - "service": [], - "verificationMethod": [ - { - "id": "#signingKey", - "controller": "did:ion:EiD9H4OHw5X4ctS1Q1G9LxmyEed9WDBW_QZ4VMpuOtRciw:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJzaWduaW5nS2V5IiwicHVibGljS2V5SndrIjp7ImNydiI6InNlY3AyNTZrMSIsImt0eSI6IkVDIiwieCI6IndkRGZEakwxRlFET3NwcC1xdmRLUUtyNzllbTdOczJFNVNBVWE5aElRaTQiLCJ5IjoiUGZmc0hEYXA1X0t3UlZwNzgtaUJaQm5XQTZMS3p6bGIxSXJ3VWhFakpuOCJ9LCJwdXJwb3NlcyI6WyJhdXRoZW50aWNhdGlvbiIsImFzc2VydGlvbk1ldGhvZCIsImNhcGFiaWxpdHlJbnZvY2F0aW9uIiwiY2FwYWJpbGl0eURlbGVnYXRpb24iLCJrZXlBZ3JlZW1lbnQiXSwidHlwZSI6IkVjZHNhU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOSJ9XSwic2VydmljZXMiOltdfX1dLCJ1cGRhdGVDb21taXRtZW50IjoiRWlBazRmbkFKSTJuZ1Z5ZjhrZ05fbUI5emhmX2FKcmdwa2tlalVIbTR1X3gzQSJ9LCJzdWZmaXhEYXRhIjp7ImRlbHRhSGFzaCI6IkVpQVRaWi1jclh5OXFYeGhGdkFFZElhU0pLY0tTWTVubkZ5bkJCSWtsODF5N1EiLCJyZWNvdmVyeUNvbW1pdG1lbnQiOiJFaUFOOHQ3UHlZYmtONFc3ZEVZX1JZX25YWUNlc1JPQl9mUWxzdWx3eVNyYVF3In0sInR5cGUiOiJjcmVhdGUifQ", - "type": "EcdsaSecp256k1VerificationKey2019", - "publicKeyJwk": { - "crv": "secp256k1", - "kty": "EC", - "x": "wdDfDjL1FQDOspp-qvdKQKr79em7Ns2E5SAUa9hIQi4", - "y": "PffsHDap5_KwRVp78-iBZBnWA6LKzzlb1IrwUhEjJn8" - } - } - ], - "authentication": [ - "#signingKey" - ], - "assertionMethod": [ - "#signingKey" - ], - "capabilityInvocation": [ - "#signingKey" - ], - "capabilityDelegation": [ - "#signingKey" - ], - "keyAgreement": [ - "#signingKey" - ] -}` - -//nolint:lll -const request = `{"delta":{"patches":[{"action":"add-services","services":[{"id":"svc","priority":0,"serviceEndpoint":[{"uri":"https://example.com"}],"type":"type"}]}],"updateCommitment":"EiDGHZRmMumPeTuIlUfQvhyexN7F_ygx84oUX17RMwbGRA"},"suffixData":{"deltaHash":"EiC3qJy6QQwwDuPlL38QR0IY0lkqnZ1wPA160uEir43_Zg","recoveryCommitment":"EiBIv4HoV0L5B13jAuOMBTOaGnP_Lx6uW5YFvzma6cJVVQ"},"type":"create"}` diff --git a/method/sidetreelongform/dochandler/protocol/nsprovider/namespaceprovider.go b/method/sidetreelongform/dochandler/protocol/nsprovider/namespaceprovider.go deleted file mode 100644 index b2646a2..0000000 --- a/method/sidetreelongform/dochandler/protocol/nsprovider/namespaceprovider.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package nsprovider - -import ( - "fmt" - "sync" - - "github.com/trustbloc/sidetree-go/pkg/api/protocol" -) - -// New creates new client version provider per namespace. -func New() *Provider { - return &Provider{clients: make(map[string]ClientVersionProvider)} -} - -// ClientVersionProvider defines interface for accessing protocol version information. -type ClientVersionProvider interface { - // Current returns latest client version. - Current() (protocol.Version, error) - - // Get returns the client version at the given transaction time. - Get(transactionTime uint64) (protocol.Version, error) -} - -// Provider implements client version provider per namespace. -type Provider struct { - mutex sync.RWMutex - clients map[string]ClientVersionProvider -} - -// Add adds client version provider for namespace. -func (m *Provider) Add(namespace string, cvp ClientVersionProvider) { - m.mutex.Lock() - defer m.mutex.Unlock() - - m.clients[namespace] = cvp -} - -// ForNamespace will return client version provider for that namespace. -func (m *Provider) ForNamespace(namespace string) (ClientVersionProvider, error) { - m.mutex.RLock() - defer m.mutex.RUnlock() - - cvp, ok := m.clients[namespace] - if !ok { - return nil, fmt.Errorf("client version(s) not defined for namespace: %s", namespace) - } - - return cvp, nil -} diff --git a/method/sidetreelongform/dochandler/protocol/nsprovider/namespaceprovider_test.go b/method/sidetreelongform/dochandler/protocol/nsprovider/namespaceprovider_test.go deleted file mode 100644 index d2241f9..0000000 --- a/method/sidetreelongform/dochandler/protocol/nsprovider/namespaceprovider_test.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package nsprovider_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - "github.com/trustbloc/sidetree-go/pkg/api/protocol" - coremocks "github.com/trustbloc/sidetree-go/pkg/mocks" - - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocol/nsprovider" - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocol/verprovider" -) - -const ns = "did:ion" - -//go:generate counterfeiter -o ./../mocks/clientversionprovider.gen.go --fake-name ClientVersionProvider . ClientVersionProvider - -func TestNew(t *testing.T) { - p := nsprovider.New() - require.NotNil(t, p) -} - -func TestClientProvider_ForNamespace(t *testing.T) { - v1_0 := &coremocks.ProtocolVersion{} - v1_0.ProtocolReturns(protocol.Protocol{ - GenesisTime: 0, - MaxOperationCount: 10, - }) - - versions := []protocol.Version{v1_0} - - p := nsprovider.New() - require.NotNil(t, p) - - verProvider, err := verprovider.New(versions) - require.NoError(t, err) - - p.Add(ns, verProvider) - - t.Run("success", func(t *testing.T) { - vp, err := p.ForNamespace(ns) - require.NoError(t, err) - require.NotNil(t, vp) - - cur, err := vp.Current() - require.NoError(t, err) - require.Equal(t, uint(10), cur.Protocol().MaxOperationCount) - }) - - t.Run("error - client versions not found for namespace", func(t *testing.T) { - vp, err := p.ForNamespace("invalid") - require.Error(t, err) - require.Nil(t, vp) - require.Contains(t, err.Error(), "client version(s) not defined for namespace: invalid") - }) -} diff --git a/method/sidetreelongform/dochandler/protocol/verprovider/versionprovider.go b/method/sidetreelongform/dochandler/protocol/verprovider/versionprovider.go deleted file mode 100644 index c178895..0000000 --- a/method/sidetreelongform/dochandler/protocol/verprovider/versionprovider.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package verprovider - -import ( - "fmt" - "sort" - - "github.com/trustbloc/sidetree-go/pkg/api/protocol" -) - -// ClientVersionProvider implements client versions. -type ClientVersionProvider struct { - versions []protocol.Version - current protocol.Version -} - -// Option is an option for client. -type Option func(opts *ClientVersionProvider) - -// WithCurrentProtocolVersion sets optional current client protocol version (defaults to last registered protocol). -func WithCurrentProtocolVersion(version string) Option { - return func(opts *ClientVersionProvider) { - for _, p := range opts.versions { - if p.Version() == version { - opts.current = p - - return - } - } - } -} - -// New creates new client version provider. -func New(clientVersions []protocol.Version, opts ...Option) (*ClientVersionProvider, error) { - if len(clientVersions) == 0 { - return nil, fmt.Errorf("must provide at least one client version") - } - - // Creating the list of the client versions - var versions []protocol.Version - - versions = append(versions, clientVersions...) - - // Sorting the client version list based on version genesis time - sort.SliceStable(versions, func(i, j int) bool { - return versions[j].Protocol().GenesisTime > versions[i].Protocol().GenesisTime - }) - - client := &ClientVersionProvider{ - versions: versions, - current: versions[len(versions)-1], - } - - // apply options - for _, opt := range opts { - opt(client) - } - - return client, nil -} - -// Current returns the latest version of client. -func (c *ClientVersionProvider) Current() (protocol.Version, error) { - return c.current, nil -} - -// Get gets client version based on version time. -func (c *ClientVersionProvider) Get(versionTime uint64) (protocol.Version, error) { - for i := len(c.versions) - 1; i >= 0; i-- { - cv := c.versions[i] - p := cv.Protocol() - - if versionTime == p.GenesisTime { - return cv, nil - } - } - - return nil, fmt.Errorf("client version is not defined for version genesis time: %d", versionTime) -} diff --git a/method/sidetreelongform/dochandler/protocol/verprovider/versionprovider_test.go b/method/sidetreelongform/dochandler/protocol/verprovider/versionprovider_test.go deleted file mode 100644 index 2e9613b..0000000 --- a/method/sidetreelongform/dochandler/protocol/verprovider/versionprovider_test.go +++ /dev/null @@ -1,125 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package verprovider_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - "github.com/trustbloc/sidetree-go/pkg/api/protocol" - coremocks "github.com/trustbloc/sidetree-go/pkg/mocks" - - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocol/verprovider" -) - -func TestVersionprovider_New(t *testing.T) { - t.Run("success", func(t *testing.T) { - v1_0 := &coremocks.ProtocolVersion{} - v1_0.ProtocolReturns(protocol.Protocol{ - GenesisTime: 1, - MultihashAlgorithms: []uint{18}, - MaxOperationSize: 2000, - MaxOperationCount: 10000, - }) - - vp, err := verprovider.New([]protocol.Version{v1_0}) - require.NotNil(t, vp) - require.NoError(t, err) - }) - - t.Run("error", func(t *testing.T) { - client, err := verprovider.New(nil) - require.Nil(t, client) - require.Error(t, err) - require.Contains(t, err.Error(), "must provide at least one client version") - }) -} - -func TestClient_Current(t *testing.T) { - v1_0 := &coremocks.ProtocolVersion{} - v1_0.VersionReturns("1.0") - v1_0.ProtocolReturns(protocol.Protocol{ - GenesisTime: 1, - MultihashAlgorithms: []uint{18}, - MaxOperationSize: 2000, - MaxOperationCount: 10000, - }) - - v0_1 := &coremocks.ProtocolVersion{} - v0_1.VersionReturns("0.1") - v0_1.ProtocolReturns(protocol.Protocol{ - GenesisTime: 0, - MultihashAlgorithms: []uint{18}, - MaxOperationSize: 500, - MaxOperationCount: 100, - }) - - t.Run("success - default", func(t *testing.T) { - versions := []protocol.Version{v1_0, v0_1} - - vp, err := verprovider.New(versions) - require.NotNil(t, vp) - require.NoError(t, err) - - p, err := vp.Current() - require.NoError(t, err) - require.Equal(t, uint(10000), p.Protocol().MaxOperationCount) - }) - - t.Run("success - with current protocol version", func(t *testing.T) { - versions := []protocol.Version{v0_1, v1_0} - - vp, err := verprovider.New(versions, verprovider.WithCurrentProtocolVersion("0.1")) - require.NotNil(t, vp) - require.NoError(t, err) - - p, err := vp.Current() - require.NoError(t, err) - require.Equal(t, uint(100), p.Protocol().MaxOperationCount) - }) -} - -func TestClientVersionProvider_Get(t *testing.T) { - v1_0 := &coremocks.ProtocolVersion{} - v1_0.VersionReturns("1.0") - v1_0.ProtocolReturns(protocol.Protocol{ - GenesisTime: 1, - MultihashAlgorithms: []uint{18}, - MaxOperationSize: 2000, - MaxOperationCount: 10000, - }) - - v0_1 := &coremocks.ProtocolVersion{} - v0_1.VersionReturns("0.1") - v0_1.ProtocolReturns(protocol.Protocol{ - GenesisTime: 0, - MultihashAlgorithms: []uint{18}, - MaxOperationSize: 500, - MaxOperationCount: 100, - }) - - versions := []protocol.Version{v1_0, v0_1} - - vp, err := verprovider.New(versions) - require.NoError(t, err) - require.NotNil(t, vp) - - v, err := vp.Get(0) - require.NoError(t, err) - require.Equal(t, uint(100), v.Protocol().MaxOperationCount) - require.Equal(t, "0.1", v.Version()) - - v, err = vp.Get(1) - require.NoError(t, err) - require.Equal(t, uint(10000), v.Protocol().MaxOperationCount) - require.Equal(t, "1.0", v.Version()) - - v, err = vp.Get(5) - require.Error(t, err) - require.Nil(t, v) - require.Equal(t, err.Error(), "client version is not defined for version genesis time: 5") -} diff --git a/method/sidetreelongform/dochandler/protocolversion/clientregistry/clientregistry.go b/method/sidetreelongform/dochandler/protocolversion/clientregistry/clientregistry.go deleted file mode 100644 index e1f2321..0000000 --- a/method/sidetreelongform/dochandler/protocolversion/clientregistry/clientregistry.go +++ /dev/null @@ -1,71 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package clientregistry - -import ( - "fmt" - "sync" - - "github.com/trustbloc/sidetree-go/pkg/api/protocol" - - vercommon "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/common" - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/versions/common" -) - -type factory interface { - Create(version string, config *common.ProtocolConfig) (protocol.Version, error) -} - -// Registry implements a client version factory registry. -type Registry struct { - factories map[string]factory - mutex sync.RWMutex -} - -// New returns a new client version factory Registry. -func New() *Registry { - registry := &Registry{factories: make(map[string]factory)} - - addVersions(registry) - - return registry -} - -// CreateClientVersion creates a new client version using the given version and providers. -func (r *Registry) CreateClientVersion(version string, config *common.ProtocolConfig) (protocol.Version, error) { - v, err := r.resolveFactory(version) - if err != nil { - return nil, err - } - - return v.Create(version, config) -} - -// Register registers a client factory for a given version. -func (r *Registry) Register(version string, factory factory) { - r.mutex.Lock() - defer r.mutex.Unlock() - - if _, ok := r.factories[version]; ok { - panic(fmt.Errorf("client version factory [%s] already registered", version)) - } - - r.factories[version] = factory -} - -func (r *Registry) resolveFactory(version string) (factory, error) { - r.mutex.RLock() - defer r.mutex.RUnlock() - - for v, f := range r.factories { - if vercommon.Version(v).Matches(version) { - return f, nil - } - } - - return nil, fmt.Errorf("client version factory for version [%s] not found", version) -} diff --git a/method/sidetreelongform/dochandler/protocolversion/clientregistry/clientregistry_test.go b/method/sidetreelongform/dochandler/protocolversion/clientregistry/clientregistry_test.go deleted file mode 100644 index 4595393..0000000 --- a/method/sidetreelongform/dochandler/protocolversion/clientregistry/clientregistry_test.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package clientregistry_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - coremocks "github.com/trustbloc/sidetree-go/pkg/mocks" - - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/clientregistry" - crmocks "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/clientregistry/mocks" - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/versions/common" -) - -//go:generate counterfeiter -o ./mocks/clientfactory.gen.go --fake-name ClientFactory . factory - -func TestRegistry(t *testing.T) { - const version = "0.1" - - f := &crmocks.ClientFactory{} - f.CreateReturns(&coremocks.ProtocolVersion{}, nil) - - r := clientregistry.New() - - require.NotPanics(t, func() { r.Register(version, f) }) - require.PanicsWithError(t, "client version factory [0.1] already registered", func() { r.Register(version, f) }) - - pv, err := r.CreateClientVersion(version, &common.ProtocolConfig{}) - require.NoError(t, err) - require.NotNil(t, pv) - - pv, err = r.CreateClientVersion("99", &common.ProtocolConfig{}) - require.EqualError(t, err, "client version factory for version [99] not found") - require.Nil(t, pv) -} diff --git a/method/sidetreelongform/dochandler/protocolversion/clientregistry/mocks/clientfactory.gen.go b/method/sidetreelongform/dochandler/protocolversion/clientregistry/mocks/clientfactory.gen.go deleted file mode 100644 index 874f73f..0000000 --- a/method/sidetreelongform/dochandler/protocolversion/clientregistry/mocks/clientfactory.gen.go +++ /dev/null @@ -1,117 +0,0 @@ -// Code generated by counterfeiter. DO NOT EDIT. -package mocks - -import ( - "sync" - - "github.com/trustbloc/sidetree-go/pkg/api/protocol" - - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/versions/common" -) - -type ClientFactory struct { - CreateStub func(string, *common.ProtocolConfig) (protocol.Version, error) - createMutex sync.RWMutex - createArgsForCall []struct { - arg1 string - arg2 *common.ProtocolConfig - } - createReturns struct { - result1 protocol.Version - result2 error - } - createReturnsOnCall map[int]struct { - result1 protocol.Version - result2 error - } - invocations map[string][][]interface{} - invocationsMutex sync.RWMutex -} - -func (fake *ClientFactory) Create(arg1 string, arg2 *common.ProtocolConfig) (protocol.Version, error) { - fake.createMutex.Lock() - ret, specificReturn := fake.createReturnsOnCall[len(fake.createArgsForCall)] - fake.createArgsForCall = append(fake.createArgsForCall, struct { - arg1 string - arg2 *common.ProtocolConfig - }{arg1, arg2}) - fake.recordInvocation("Create", []interface{}{arg1, arg2}) - fake.createMutex.Unlock() - if fake.CreateStub != nil { - return fake.CreateStub(arg1, arg2) - } - if specificReturn { - return ret.result1, ret.result2 - } - fakeReturns := fake.createReturns - return fakeReturns.result1, fakeReturns.result2 -} - -func (fake *ClientFactory) CreateCallCount() int { - fake.createMutex.RLock() - defer fake.createMutex.RUnlock() - return len(fake.createArgsForCall) -} - -func (fake *ClientFactory) CreateCalls(stub func(string, *common.ProtocolConfig) (protocol.Version, error)) { - fake.createMutex.Lock() - defer fake.createMutex.Unlock() - fake.CreateStub = stub -} - -func (fake *ClientFactory) CreateArgsForCall(i int) (string, *common.ProtocolConfig) { - fake.createMutex.RLock() - defer fake.createMutex.RUnlock() - argsForCall := fake.createArgsForCall[i] - return argsForCall.arg1, argsForCall.arg2 -} - -func (fake *ClientFactory) CreateReturns(result1 protocol.Version, result2 error) { - fake.createMutex.Lock() - defer fake.createMutex.Unlock() - fake.CreateStub = nil - fake.createReturns = struct { - result1 protocol.Version - result2 error - }{result1, result2} -} - -func (fake *ClientFactory) CreateReturnsOnCall(i int, result1 protocol.Version, result2 error) { - fake.createMutex.Lock() - defer fake.createMutex.Unlock() - fake.CreateStub = nil - if fake.createReturnsOnCall == nil { - fake.createReturnsOnCall = make(map[int]struct { - result1 protocol.Version - result2 error - }) - } - fake.createReturnsOnCall[i] = struct { - result1 protocol.Version - result2 error - }{result1, result2} -} - -func (fake *ClientFactory) Invocations() map[string][][]interface{} { - fake.invocationsMutex.RLock() - defer fake.invocationsMutex.RUnlock() - fake.createMutex.RLock() - defer fake.createMutex.RUnlock() - copiedInvocations := map[string][][]interface{}{} - for key, value := range fake.invocations { - copiedInvocations[key] = value - } - return copiedInvocations -} - -func (fake *ClientFactory) recordInvocation(key string, args []interface{}) { - fake.invocationsMutex.Lock() - defer fake.invocationsMutex.Unlock() - if fake.invocations == nil { - fake.invocations = map[string][][]interface{}{} - } - if fake.invocations[key] == nil { - fake.invocations[key] = [][]interface{}{} - } - fake.invocations[key] = append(fake.invocations[key], args) -} diff --git a/method/sidetreelongform/dochandler/protocolversion/clientregistry/testversions.go b/method/sidetreelongform/dochandler/protocolversion/clientregistry/testversions.go deleted file mode 100644 index 8561b26..0000000 --- a/method/sidetreelongform/dochandler/protocolversion/clientregistry/testversions.go +++ /dev/null @@ -1,29 +0,0 @@ -//go:build testver -// +build testver - -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package clientregistry - -import ( - v1_0 "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/client" -) - -const ( - // V1_0 ... - V1_0 = "1.0" - - test = "test" -) - -func addVersions(registry *Registry) { - // register supported versions - registry.Register(V1_0, v1_0.New()) - - // used for test only - registry.Register(test, v_test.New()) -} diff --git a/method/sidetreelongform/dochandler/protocolversion/clientregistry/versions.go b/method/sidetreelongform/dochandler/protocolversion/clientregistry/versions.go deleted file mode 100644 index bee8cb8..0000000 --- a/method/sidetreelongform/dochandler/protocolversion/clientregistry/versions.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build !testver -// +build !testver - -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package clientregistry - -import v1_0 "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/client" - -const ( - // V1_0 ... - V1_0 = "1.0" -) - -func addVersions(registry *Registry) { - // register supported versions - registry.Register(V1_0, v1_0.New()) -} diff --git a/method/sidetreelongform/dochandler/protocolversion/common/version.go b/method/sidetreelongform/dochandler/protocolversion/common/version.go deleted file mode 100644 index 3713645..0000000 --- a/method/sidetreelongform/dochandler/protocolversion/common/version.go +++ /dev/null @@ -1,71 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package common - -import ( - "errors" - "strings" -) - -// Version represents the protocol version string. -type Version string - -// Matches returns true if the major and minor versions match. For example: -// -// 'v1' and 'v1.2.0' => false -// 'v1' and 'v1.0.0' => true -// 'v1' and 'v1.0.1' => true -// 'v1.0' and 'v1.0.1' => true -// 'v1.1' and 'v1.1.4' => true -// 'v1.1' and 'v1.2.0' => false. -func (v Version) Matches(other string) bool { - p1 := strings.Split(string(v), ".") - p2 := strings.Split(other, ".") - - var majorVersion1 string - - minorVersion1 := "0" - - var majorVersion2 string - - minorVersion2 := "0" - - if len(p1) > 0 { - majorVersion1 = p1[0] - } - - if len(p1) > 1 { - minorVersion1 = p1[1] - } - - if len(p2) > 0 { - majorVersion2 = p2[0] - } - - if len(p2) > 1 { - minorVersion2 = p2[1] - } - - return majorVersion1 == majorVersion2 && minorVersion1 == minorVersion2 -} - -// Validate validates the format of the version string. -func (v Version) Validate() error { - p := strings.Split(string(v), ".") - - if len(p) == 0 || p[0] == "" { - return errors.New("no version specified") - } - - const v2 = 2 - - if len(p) > v2 { - return errors.New("version must only have a major and optional minor part (e.g. v1 or v1.1)") - } - - return nil -} diff --git a/method/sidetreelongform/dochandler/protocolversion/common/version_test.go b/method/sidetreelongform/dochandler/protocolversion/common/version_test.go deleted file mode 100644 index 5e99de4..0000000 --- a/method/sidetreelongform/dochandler/protocolversion/common/version_test.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package common_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/common" -) - -func TestCcVersion_Matches(t *testing.T) { - v1 := common.Version("v1") - v1_0 := common.Version("v1.0") - v1_1 := common.Version("v1.1") - v1_2 := common.Version("v1.2") - - require.True(t, v1.Matches("v1.0.0")) - require.True(t, v1.Matches("v1.0.1")) - require.True(t, v1_0.Matches("v1.0.1")) - require.False(t, v1.Matches("v1.2.1")) - require.True(t, v1_2.Matches("v1.2.1")) - require.False(t, v1_1.Matches("v1.2.0")) -} - -func TestCcVersion_Validate(t *testing.T) { - v := common.Version("") - v1 := common.Version("v1") - v1_0 := common.Version("v1.0") - v1_0_0 := common.Version("v1.0.0") - - require.EqualError(t, v.Validate(), "no version specified") - require.NoError(t, v1.Validate()) - require.NoError(t, v1_0.Validate()) - require.EqualError(t, v1_0_0.Validate(), "version must only have a major and optional minor part (e.g. v1 or v1.1)") -} diff --git a/method/sidetreelongform/dochandler/protocolversion/versions/common/protocol.go b/method/sidetreelongform/dochandler/protocolversion/versions/common/protocol.go deleted file mode 100644 index 547226f..0000000 --- a/method/sidetreelongform/dochandler/protocolversion/versions/common/protocol.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package common - -import ( - "github.com/trustbloc/sidetree-go/pkg/api/protocol" -) - -// ProtocolVersion implements the protocol.Version interface. -type ProtocolVersion struct { - VersionStr string - P protocol.Protocol - OpParser protocol.OperationParser - OpApplier protocol.OperationApplier - DocValidator protocol.DocumentValidator - DocTransformer protocol.DocumentTransformer -} - -// Version returns the protocol parameters. -func (h *ProtocolVersion) Version() string { - return h.VersionStr -} - -// Protocol returns the protocol parameters. -func (h *ProtocolVersion) Protocol() protocol.Protocol { - return h.P -} - -// OperationParser returns the operation parser. -func (h *ProtocolVersion) OperationParser() protocol.OperationParser { - return h.OpParser -} - -// OperationApplier returns the operation applier. -func (h *ProtocolVersion) OperationApplier() protocol.OperationApplier { - return h.OpApplier -} - -// DocumentValidator returns the document validator. -func (h *ProtocolVersion) DocumentValidator() protocol.DocumentValidator { - return h.DocValidator -} - -// DocumentTransformer returns the document transformer. -func (h *ProtocolVersion) DocumentTransformer() protocol.DocumentTransformer { - return h.DocTransformer -} - -// ProtocolConfig hold setting for client protocol configuration. -type ProtocolConfig struct { - MethodContext []string - EnableBase bool -} diff --git a/method/sidetreelongform/dochandler/protocolversion/versions/common/protocol_test.go b/method/sidetreelongform/dochandler/protocolversion/versions/common/protocol_test.go deleted file mode 100644 index b633985..0000000 --- a/method/sidetreelongform/dochandler/protocolversion/versions/common/protocol_test.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package common_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - "github.com/trustbloc/sidetree-go/pkg/api/protocol" - coremocks "github.com/trustbloc/sidetree-go/pkg/mocks" - - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/versions/common" -) - -func TestProtocolVersion(t *testing.T) { - p := &common.ProtocolVersion{ - VersionStr: "1.1", - P: protocol.Protocol{ - GenesisTime: 1000, - }, - OpParser: &coremocks.OperationParser{}, - OpApplier: &coremocks.OperationApplier{}, - DocValidator: &coremocks.DocumentValidator{}, - } - - require.Equal(t, p.VersionStr, p.Version()) - require.Equal(t, p.P, p.Protocol()) - require.Equal(t, p.OpParser, p.OperationParser()) - require.Equal(t, p.OpApplier, p.OperationApplier()) - require.Equal(t, p.DocValidator, p.DocumentValidator()) -} diff --git a/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/client/client.go b/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/client/client.go deleted file mode 100644 index f28b12c..0000000 --- a/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/client/client.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package client - -import ( - "github.com/trustbloc/sidetree-go/pkg/api/protocol" - "github.com/trustbloc/sidetree-go/pkg/versions/1_0/doccomposer" - "github.com/trustbloc/sidetree-go/pkg/versions/1_0/doctransformer/didtransformer" - "github.com/trustbloc/sidetree-go/pkg/versions/1_0/docvalidator/didvalidator" - "github.com/trustbloc/sidetree-go/pkg/versions/1_0/operationapplier" - "github.com/trustbloc/sidetree-go/pkg/versions/1_0/operationparser" - - vcommon "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/versions/common" - protocolcfg "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/config" -) - -// Factory implements version 0.1 of the client factory. -type Factory struct{} - -// New returns a version 1.0 implementation of the Sidetree protocol. -func New() *Factory { - return &Factory{} -} - -// Create returns a 1.0 client version. -func (v *Factory) Create(version string, config *vcommon.ProtocolConfig) (protocol.Version, error) { - p := protocolcfg.GetProtocolConfig() - - op := operationparser.New(p) - - dc := doccomposer.New() - oa := operationapplier.New(p, op, dc) - - dv := didvalidator.New() - dt := didtransformer.New( - didtransformer.WithMethodContext(config.MethodContext), - didtransformer.WithBase(config.EnableBase)) - - return &vcommon.ProtocolVersion{ - VersionStr: version, - P: p, - OpParser: op, - OpApplier: oa, - DocValidator: dv, - DocTransformer: dt, - }, nil -} diff --git a/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/client/client_test.go b/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/client/client_test.go deleted file mode 100644 index eb3fd6f..0000000 --- a/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/client/client_test.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package client_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/versions/common" - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/client" -) - -func TestFactory_Create(t *testing.T) { - f := client.New() - require.NotNil(t, f) - - t.Run("success", func(t *testing.T) { - pv, err := f.Create("1.0", &common.ProtocolConfig{}) - require.NoError(t, err) - require.NotNil(t, pv) - }) -} diff --git a/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/config/protocol.go b/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/config/protocol.go deleted file mode 100644 index 572f5bc..0000000 --- a/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/config/protocol.go +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package config - -import ( - "github.com/trustbloc/sidetree-go/pkg/api/protocol" -) - -// GetProtocolConfig returns protocol config for this version. -func GetProtocolConfig() protocol.Protocol { - p := protocol.Protocol{ - GenesisTime: 0, - MultihashAlgorithms: []uint{18}, - MaxOperationCount: 10000, - MaxOperationSize: 2500, - MaxOperationHashLength: 100, - MaxDeltaSize: 1700, - MaxCasURILength: 500, - CompressionAlgorithm: "GZIP", - MaxChunkFileSize: 10000000, - MaxProvisionalIndexFileSize: 1000000, - MaxCoreIndexFileSize: 1000000, - MaxProofFileSize: 2500000, - Patches: []string{"replace", "add-public-keys", "remove-public-keys", "add-services", "remove-services", "add-also-known-as", "remove-also-known-as"}, //nolint:lll - SignatureAlgorithms: []string{"EdDSA", "ES256", "ES256K"}, - KeyAlgorithms: []string{"Ed25519", "P-256", "P-384", "secp256k1"}, - MaxMemoryDecompressionFactor: 3, - NonceSize: 16, - } - - return p -} diff --git a/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/config/protocol_test.go b/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/config/protocol_test.go deleted file mode 100644 index 889b48f..0000000 --- a/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/config/protocol_test.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package config_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler/protocolversion/versions/v1_0/config" -) - -func TestGetProtocolConfig(t *testing.T) { - t.Run("success - maximum operation count", func(t *testing.T) { - cfg := config.GetProtocolConfig() - require.Equal(t, uint(10000), cfg.MaxOperationCount) - }) - - t.Run("success - key algorithms", func(t *testing.T) { - cfg := config.GetProtocolConfig() - require.Equal(t, []string{"Ed25519", "P-256", "P-384", "secp256k1"}, cfg.KeyAlgorithms) - }) -} diff --git a/method/sidetreelongform/sidetree/api/api.go b/method/sidetreelongform/sidetree/api/api.go deleted file mode 100644 index 46cc1b2..0000000 --- a/method/sidetreelongform/sidetree/api/api.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -// Package api include interface -package api - -import "github.com/trustbloc/sidetree-go/pkg/jws" - -// Signer defines JWS Signer interface that will be used to sign required data in Sidetree request. -type Signer interface { - // Sign signs data and returns signature value - Sign(data []byte) ([]byte, error) - - // Headers provides required JWS protected headers. It provides information about signing key and algorithm. - Headers() jws.Headers - - // PublicKeyJWK return public key in JWK format - PublicKeyJWK() *jws.JWK -} diff --git a/method/sidetreelongform/sidetree/client.go b/method/sidetreelongform/sidetree/client.go deleted file mode 100644 index 6866e80..0000000 --- a/method/sidetreelongform/sidetree/client.go +++ /dev/null @@ -1,668 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -// Package sidetree implements sidetree client -package sidetree - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "log" - "net/http" - "os" - "strings" - - "github.com/trustbloc/sidetree-go/pkg/commitment" - "github.com/trustbloc/sidetree-go/pkg/hashing" - "github.com/trustbloc/sidetree-go/pkg/patch" - "github.com/trustbloc/sidetree-go/pkg/util/pubkey" - "github.com/trustbloc/sidetree-go/pkg/versions/1_0/client" - - docdid "github.com/trustbloc/did-go/doc/did" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/doc" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/option/create" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/option/deactivate" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/option/recovery" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/option/update" -) - -const ( - defaultHashAlgorithm = 18 - logPrefix = " [did-go/method/sidetree] " -) - -var errorLogger = log.New(os.Stderr, logPrefix, log.Ldate|log.Ltime|log.LUTC) -var debugLogger = log.New(io.Discard, logPrefix, log.Ldate|log.Ltime|log.LUTC) - -// SetDebugOutput is used to set output of debug logs. -func SetDebugOutput(out io.Writer) { - debugLogger.SetOutput(out) -} - -type authTokenProvider interface { - AuthToken() (string, error) -} - -// GetEndpointsFunc retrieves sidetree endpoints. -type GetEndpointsFunc = func(disableCache bool) ([]string, error) - -// Client sidetree client. -type Client struct { - client *http.Client - authToken string - authTokenProvider authTokenProvider - sendRequest func(req []byte, getEndpoints GetEndpointsFunc) ([]byte, error) -} - -// New return sidetree client. -func New(opts ...Option) *Client { - c := &Client{client: &http.Client{}} - - c.sendRequest = c.defaultSendRequest - - // Apply options - for _, opt := range opts { - opt(c) - } - - return c -} - -// CreateDID create did doc. -func (c *Client) CreateDID(opts ...create.Option) (*docdid.DocResolution, error) { - createDIDOpts := &create.Opts{MultiHashAlgorithm: defaultHashAlgorithm} - // Apply options - for _, opt := range opts { - opt(createDIDOpts) - } - - err := validateCreateReq(createDIDOpts) - if err != nil { - return nil, err - } - - req, err := buildCreateRequest(createDIDOpts.MultiHashAlgorithm, createDIDOpts) - if err != nil { - return nil, fmt.Errorf("failed to build sidetree request: %w", err) - } - - responseBytes, err := c.sendRequest(req, createDIDOpts.GetEndpoints) - if err != nil { - return nil, fmt.Errorf("failed to send create sidetree request: %w", err) - } - - documentResolution, err := docdid.ParseDocumentResolution(responseBytes) - if err != nil { - if !errors.Is(err, docdid.ErrDIDDocumentNotExist) { - return nil, fmt.Errorf("failed to parse document resolution: %w", err) - } - - errorLogger.Printf("failed to parse document resolution %v", err) - } else { - return documentResolution, nil - } - - didDoc, err := docdid.ParseDocument(responseBytes) - if err != nil { - return nil, fmt.Errorf("failed to parse did document: %w", err) - } - - return &docdid.DocResolution{DIDDocument: didDoc}, nil -} - -// UpdateDID update did doc. -func (c *Client) UpdateDID(did string, opts ...update.Option) error { - updateDIDOpts := &update.Opts{MultiHashAlgorithm: defaultHashAlgorithm} - // Apply options - for _, opt := range opts { - opt(updateDIDOpts) - } - - err := validateUpdateReq(updateDIDOpts) - if err != nil { - return err - } - - req, err := c.buildUpdateRequest(did, updateDIDOpts.MultiHashAlgorithm, updateDIDOpts) - if err != nil { - return fmt.Errorf("failed to build update request: %w", err) - } - - _, err = c.sendRequest(req, updateDIDOpts.GetEndpoints) - if err != nil { - return fmt.Errorf("failed to send update did request: %w", err) - } - - return nil -} - -// RecoverDID recover did doc. -func (c *Client) RecoverDID(did string, opts ...recovery.Option) error { - recoverDIDOpts := &recovery.Opts{MultiHashAlgorithm: defaultHashAlgorithm} - // Apply options - for _, opt := range opts { - opt(recoverDIDOpts) - } - - err := validateRecoverReq(recoverDIDOpts) - if err != nil { - return err - } - - req, err := buildRecoverRequest(did, recoverDIDOpts.MultiHashAlgorithm, recoverDIDOpts) - if err != nil { - return fmt.Errorf("failed to build sidetree request: %w", err) - } - - _, err = c.sendRequest(req, recoverDIDOpts.GetEndpoints) - if err != nil { - return fmt.Errorf("failed to send recover sidetree request: %w", err) - } - - return err -} - -// DeactivateDID deactivate did doc. -func (c *Client) DeactivateDID(did string, opts ...deactivate.Option) error { - deactivateDIDOpts := &deactivate.Opts{} - // Apply options - for _, opt := range opts { - opt(deactivateDIDOpts) - } - - err := validateDeactivateReq(deactivateDIDOpts) - if err != nil { - return err - } - - req, err := buildDeactivateRequest(did, deactivateDIDOpts) - if err != nil { - return fmt.Errorf("failed to build sidetree request: %w", err) - } - - _, err = c.sendRequest(req, deactivateDIDOpts.GetEndpoints) - if err != nil { - return fmt.Errorf("failed to send deactivate sidetree request: %w", err) - } - - return err -} - -func validateCreateReq(createDIDOpts *create.Opts) error { - if createDIDOpts.RecoveryPublicKey == nil { - return fmt.Errorf("recovery public key is required") - } - - if createDIDOpts.UpdatePublicKey == nil { - return fmt.Errorf("update public key is required") - } - - return nil -} - -func validateUpdateReq(updateDIDOpts *update.Opts) error { - if updateDIDOpts.Signer == nil { - return fmt.Errorf("signer is required") - } - - if updateDIDOpts.NextUpdatePublicKey == nil { - return fmt.Errorf("next update public key is required") - } - - if updateDIDOpts.OperationCommitment == "" { - return fmt.Errorf("operation commitment is required") - } - - return nil -} - -func validateRecoverReq(recoverDIDOpts *recovery.Opts) error { - if recoverDIDOpts.NextRecoveryPublicKey == nil { - return fmt.Errorf("next recovery public key is required") - } - - if recoverDIDOpts.NextUpdatePublicKey == nil { - return fmt.Errorf("next update public key is required") - } - - if recoverDIDOpts.Signer == nil { - return fmt.Errorf("signer is required") - } - - if recoverDIDOpts.OperationCommitment == "" { - return fmt.Errorf("operation commitment is required") - } - - return nil -} - -func validateDeactivateReq(deactivateDIDOpts *deactivate.Opts) error { - if deactivateDIDOpts.Signer == nil { - return fmt.Errorf("signer is required") - } - - if deactivateDIDOpts.OperationCommitment == "" { - return fmt.Errorf("operation commitment is required") - } - - return nil -} - -// buildCreateRequest request builder for sidetree public DID creation. -func buildCreateRequest(multiHashAlgorithm uint, createDIDOpts *create.Opts) ([]byte, error) { - didDoc := &doc.Doc{ - PublicKey: createDIDOpts.PublicKeys, - Service: createDIDOpts.Services, - AlsoKnownAs: createDIDOpts.AlsoKnownAs, - } - - docBytes, err := didDoc.JSONBytes() - if err != nil { - return nil, fmt.Errorf("failed to get document bytes : %w", err) - } - - recoveryKey, err := pubkey.GetPublicKeyJWK(createDIDOpts.RecoveryPublicKey) - if err != nil { - return nil, fmt.Errorf("failed to get recovery key : %w", err) - } - - updateKey, err := pubkey.GetPublicKeyJWK(createDIDOpts.UpdatePublicKey) - if err != nil { - return nil, fmt.Errorf("failed to get update key : %w", err) - } - - recoveryCommitment, err := commitment.GetCommitment(recoveryKey, multiHashAlgorithm) - if err != nil { - return nil, err - } - - updateCommitment, err := commitment.GetCommitment(updateKey, multiHashAlgorithm) - if err != nil { - return nil, err - } - - createRequestInfo := &client.CreateRequestInfo{ - OpaqueDocument: string(docBytes), - RecoveryCommitment: recoveryCommitment, - UpdateCommitment: updateCommitment, - MultihashCode: multiHashAlgorithm, - } - - if createDIDOpts.AnchorOrigin != "" { - createRequestInfo.AnchorOrigin = createDIDOpts.AnchorOrigin - } - - req, err := client.NewCreateRequest(createRequestInfo) - if err != nil { - return nil, fmt.Errorf("failed to create sidetree request: %w", err) - } - - return req, nil -} - -// buildUpdateRequest request builder for sidetree public DID update. -func (c *Client) buildUpdateRequest(did string, multiHashAlgorithm uint, - updateDIDOpts *update.Opts) ([]byte, error) { - nextUpdateKey, err := pubkey.GetPublicKeyJWK(updateDIDOpts.NextUpdatePublicKey) - if err != nil { - return nil, fmt.Errorf("failed to get next update key : %w", err) - } - - nextUpdateCommitment, err := commitment.GetCommitment(nextUpdateKey, multiHashAlgorithm) - if err != nil { - return nil, err - } - - patches, err := createUpdatePatches(updateDIDOpts) - if err != nil { - return nil, err - } - - didSuffix, err := getUniqueSuffix(did) - if err != nil { - return nil, err - } - - multihashCode, err := hashing.GetMultihashCode(updateDIDOpts.OperationCommitment) - if err != nil { - return nil, err - } - - rv, err := commitment.GetRevealValue(updateDIDOpts.Signer.PublicKeyJWK(), uint(multihashCode)) - if err != nil { - return nil, err - } - - return client.NewUpdateRequest(&client.UpdateRequestInfo{ - DidSuffix: didSuffix, - RevealValue: rv, - UpdateCommitment: nextUpdateCommitment, - UpdateKey: updateDIDOpts.Signer.PublicKeyJWK(), - Patches: patches, - MultihashCode: multiHashAlgorithm, - Signer: updateDIDOpts.Signer, - }) -} - -// buildRecoverRequest request builder for sidetree public DID recovery. -func buildRecoverRequest(did string, multiHashAlgorithm uint, recoverDIDOpts *recovery.Opts) ([]byte, error) { - didDoc := &doc.Doc{ - PublicKey: recoverDIDOpts.PublicKeys, - Service: recoverDIDOpts.Services, - AlsoKnownAs: recoverDIDOpts.AlsoKnownAs, - } - - docBytes, err := didDoc.JSONBytes() - if err != nil { - return nil, fmt.Errorf("failed to get document bytes : %w", err) - } - - nextRecoveryCommitment, nextUpdateCommitment, err := getCommitment(multiHashAlgorithm, recoverDIDOpts) - if err != nil { - return nil, err - } - - didSuffix, err := getUniqueSuffix(did) - if err != nil { - return nil, err - } - - multihashCode, err := hashing.GetMultihashCode(recoverDIDOpts.OperationCommitment) - if err != nil { - return nil, err - } - - rv, err := commitment.GetRevealValue(recoverDIDOpts.Signer.PublicKeyJWK(), uint(multihashCode)) - if err != nil { - return nil, err - } - - recoverRequestInfo := &client.RecoverRequestInfo{ - DidSuffix: didSuffix, RevealValue: rv, OpaqueDocument: string(docBytes), - RecoveryCommitment: nextRecoveryCommitment, UpdateCommitment: nextUpdateCommitment, - MultihashCode: multiHashAlgorithm, Signer: recoverDIDOpts.Signer, - RecoveryKey: recoverDIDOpts.Signer.PublicKeyJWK(), - } - - if recoverDIDOpts.AnchorOrigin != "" { - recoverRequestInfo.AnchorOrigin = recoverDIDOpts.AnchorOrigin - } - - req, err := client.NewRecoverRequest(recoverRequestInfo) - if err != nil { - return nil, fmt.Errorf("failed to create sidetree request: %w", err) - } - - return req, nil -} - -// buildDeactivateRequest request builder for sidetree public DID deactivate. -func buildDeactivateRequest(did string, deactivateDIDOpts *deactivate.Opts) ([]byte, error) { - didSuffix, err := getUniqueSuffix(did) - if err != nil { - return nil, err - } - - multihashCode, err := hashing.GetMultihashCode(deactivateDIDOpts.OperationCommitment) - if err != nil { - return nil, err - } - - rv, err := commitment.GetRevealValue(deactivateDIDOpts.Signer.PublicKeyJWK(), uint(multihashCode)) - if err != nil { - return nil, err - } - - return client.NewDeactivateRequest(&client.DeactivateRequestInfo{ - DidSuffix: didSuffix, - RevealValue: rv, - RecoveryKey: deactivateDIDOpts.Signer.PublicKeyJWK(), - Signer: deactivateDIDOpts.Signer, - }) -} - -func (c *Client) defaultSendRequest(req []byte, getEndpoints GetEndpointsFunc) ([]byte, error) { - if getEndpoints == nil { - return nil, fmt.Errorf("sidetree get endpoints func is required") - } - - responseBytes, err := c.doSendRequest(req, getEndpoints, false) - if err != nil { - errorLogger.Printf("Error sending request. Trying again with endpoint cache disabled.: %s", err) - - responseBytes, err = c.doSendRequest(req, getEndpoints, true) - if err != nil { - return nil, fmt.Errorf("sidetree get endpoints: %w", err) - } - } - - return responseBytes, nil -} - -func (c *Client) doSendRequest(req []byte, getEndpoints GetEndpointsFunc, disableCache bool) ([]byte, error) { - endpoints, err := getEndpoints(disableCache) - if err != nil { - return nil, fmt.Errorf("sidetree get endpoints: %w", err) - } - - debugLogger.Printf("Got sidetree endpoints: %s", endpoints) - - // TODO add logic for using different sidetree endpoint - // for now will use the first one - endpointURL := endpoints[0] - - httpReq, err := http.NewRequestWithContext(context.Background(), - http.MethodPost, endpointURL, bytes.NewReader(req)) - if err != nil { - return nil, fmt.Errorf("failed to create http request: %w", err) - } - - httpReq.Header.Set("Content-Type", "application/json") - - authToken := c.authToken - - if c.authTokenProvider != nil { - v, errToken := c.authTokenProvider.AuthToken() - if errToken != nil { - return nil, errToken - } - - authToken = "Bearer " + v - } - - if authToken != "" { - httpReq.Header.Add("Authorization", authToken) - } - - resp, err := c.client.Do(httpReq) - if err != nil { - return nil, fmt.Errorf("failed to send request: %w", err) - } - - defer closeResponseBody(resp.Body) - - responseBytes, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("failed to read response : %w", err) - } - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("got unexpected response from %s status '%d' body %s", - endpointURL, resp.StatusCode, responseBytes) - } - - return responseBytes, nil -} - -func createUpdatePatches(updateDIDOpts *update.Opts) ([]patch.Patch, error) { //nolint:gocyclo - var patches []patch.Patch - - if len(updateDIDOpts.RemoveAlsoKnownAs) != 0 { - p, err := createRemoveAlsoKnownAsPatch(updateDIDOpts) - if err != nil { - return nil, err - } - - patches = append(patches, p) - } - - if len(updateDIDOpts.RemovePublicKeys) != 0 { - p, err := createRemovePublicKeysPatch(updateDIDOpts) - if err != nil { - return nil, err - } - - patches = append(patches, p) - } - - if len(updateDIDOpts.RemoveServices) != 0 { - p, err := createRemoveServicesPatch(updateDIDOpts) - if err != nil { - return nil, err - } - - patches = append(patches, p) - } - - if len(updateDIDOpts.AddAlsoKnownAs) != 0 { - p, err := createAddAlsoKnownAsPatch(updateDIDOpts) - if err != nil { - return nil, err - } - - patches = append(patches, p) - } - - if len(updateDIDOpts.AddServices) != 0 { - p, err := createAddServicesPatch(updateDIDOpts) - if err != nil { - return nil, err - } - - patches = append(patches, p) - } - - if len(updateDIDOpts.AddPublicKeys) != 0 { - p, err := createAddPublicKeysPatch(updateDIDOpts) - if err != nil { - return nil, err - } - - patches = append(patches, p) - } - - return patches, nil -} - -func createRemovePublicKeysPatch(updateDIDOpts *update.Opts) (patch.Patch, error) { - removePubKeys, err := json.Marshal(updateDIDOpts.RemovePublicKeys) - if err != nil { - return nil, err - } - - return patch.NewRemovePublicKeysPatch(string(removePubKeys)) -} - -func createRemoveServicesPatch(updateDIDOpts *update.Opts) (patch.Patch, error) { - removeServices, err := json.Marshal(updateDIDOpts.RemoveServices) - if err != nil { - return nil, err - } - - return patch.NewRemoveServiceEndpointsPatch(string(removeServices)) -} - -func createRemoveAlsoKnownAsPatch(updateDIDOpts *update.Opts) (patch.Patch, error) { - removeAlsoKnownAs, err := json.Marshal(updateDIDOpts.RemoveAlsoKnownAs) - if err != nil { - return nil, err - } - - return patch.NewRemoveAlsoKnownAs(string(removeAlsoKnownAs)) -} - -func createAddAlsoKnownAsPatch(updateDIDOpts *update.Opts) (patch.Patch, error) { - rawAlsoKnownAs := doc.PopulateRawAlsoKnownAs(updateDIDOpts.AddAlsoKnownAs) - - addAlsoKnownAs, err := json.Marshal(rawAlsoKnownAs) - if err != nil { - return nil, err - } - - return patch.NewAddAlsoKnownAs(string(addAlsoKnownAs)) -} - -func createAddServicesPatch(updateDIDOpts *update.Opts) (patch.Patch, error) { - rawServices, err := doc.PopulateRawServices(updateDIDOpts.AddServices) - if err != nil { - return nil, err - } - - addServices, err := json.Marshal(rawServices) - if err != nil { - return nil, err - } - - return patch.NewAddServiceEndpointsPatch(string(addServices)) -} - -func createAddPublicKeysPatch(updateDIDOpts *update.Opts) (patch.Patch, error) { - rawPublicKeys, err := doc.PopulateRawPublicKeys(updateDIDOpts.AddPublicKeys) - if err != nil { - return nil, err - } - - addPublicKeys, err := json.Marshal(rawPublicKeys) - if err != nil { - return nil, err - } - - return patch.NewAddPublicKeysPatch(string(addPublicKeys)) -} - -func getUniqueSuffix(id string) (string, error) { - p := strings.LastIndex(id, ":") - if p == -1 { - return "", fmt.Errorf("unique suffix not provided in id [%s]", id) - } - - return id[p+1:], nil -} - -func getCommitment(multiHashAlgorithm uint, recoverDIDOpts *recovery.Opts) (nextRecoveryCommitment string, - nextUpdateCommitment string, err error) { - nextRecoveryKey, err := pubkey.GetPublicKeyJWK(recoverDIDOpts.NextRecoveryPublicKey) - if err != nil { - return "", "", fmt.Errorf("failed to get next recovery key : %w", err) - } - - nextUpdateKey, err := pubkey.GetPublicKeyJWK(recoverDIDOpts.NextUpdatePublicKey) - if err != nil { - return "", "", fmt.Errorf("failed to get next update key : %w", err) - } - - nextRecoveryCommitment, err = commitment.GetCommitment(nextRecoveryKey, multiHashAlgorithm) - if err != nil { - return "", "", err - } - - nextUpdateCommitment, err = commitment.GetCommitment(nextUpdateKey, multiHashAlgorithm) - if err != nil { - return "", "", err - } - - return nextRecoveryCommitment, nextUpdateCommitment, nil -} - -func closeResponseBody(respBody io.Closer) { - e := respBody.Close() - if e != nil { - errorLogger.Printf("Failed to close response body: %v", e) - } -} diff --git a/method/sidetreelongform/sidetree/client_test.go b/method/sidetreelongform/sidetree/client_test.go deleted file mode 100644 index f141114..0000000 --- a/method/sidetreelongform/sidetree/client_test.go +++ /dev/null @@ -1,965 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -package sidetree_test - -import ( - "crypto" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/elliptic" - "crypto/rand" - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "testing" - - "github.com/btcsuite/btcutil/base58" - gojose "github.com/go-jose/go-jose/v3" - "github.com/stretchr/testify/require" - "github.com/trustbloc/kms-go/doc/jose/jwk" - "github.com/trustbloc/sidetree-go/pkg/commitment" - "github.com/trustbloc/sidetree-go/pkg/jws" - "github.com/trustbloc/sidetree-go/pkg/util/ecsigner" - "github.com/trustbloc/sidetree-go/pkg/util/edsigner" - "github.com/trustbloc/sidetree-go/pkg/util/pubkey" - "github.com/trustbloc/sidetree-go/pkg/versions/1_0/client" - - "github.com/trustbloc/did-go/doc/did" - model "github.com/trustbloc/did-go/doc/did/endpoint" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/doc" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/option/create" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/option/deactivate" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/option/recovery" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/option/update" -) - -type didResolution struct { - Context interface{} `json:"@context"` - DIDDocument json.RawMessage `json:"didDocument"` - ResolverMetadata json.RawMessage `json:"resolverMetadata"` - MethodMetadata json.RawMessage `json:"methodMetadata"` -} - -func TestClient_DeactivateDID(t *testing.T) { - t.Run("test signing key empty", func(t *testing.T) { - v := sidetree.New() - - err := v.DeactivateDID("did:ex:123") - require.Error(t, err) - require.Contains(t, err.Error(), "signer is required") - }) - - t.Run("test operation commitment is empty", func(t *testing.T) { - v := sidetree.New() - - _, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - err = v.DeactivateDID("did:ex:123", deactivate.WithSigner(newSignerMock(t, privKey))) - require.Error(t, err) - require.Contains(t, err.Error(), "operation commitment is required") - }) - - t.Run("test error from get endpoints", func(t *testing.T) { - v := sidetree.New() - - pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - signingPubKeyJWK, err := pubkey.GetPublicKeyJWK(pubKey) - require.NoError(t, err) - - rv, err := commitment.GetRevealValue(signingPubKeyJWK, 18) - require.NoError(t, err) - - err = v.DeactivateDID("did:ex:123", - deactivate.WithSigner(newSignerMock(t, privKey)), - deactivate.WithOperationCommitment(rv)) - require.Error(t, err) - require.Contains(t, err.Error(), "sidetree get endpoints func is required") - - err = v.DeactivateDID("did:ex:123", - deactivate.WithOperationCommitment(rv), - deactivate.WithSigner(newSignerMock(t, privKey)), - deactivate.WithSidetreeEndpoint(func(bool) ([]string, error) { - return nil, fmt.Errorf("failed to get endpoint") - })) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to get endpoint") - }) - - t.Run("test error from unique suffix", func(t *testing.T) { - v := sidetree.New() - - _, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - err = v.DeactivateDID("wrong", deactivate.WithOperationCommitment("value"), - deactivate.WithSigner(newSignerMock(t, privKey)), - deactivate.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - })) - require.Error(t, err) - require.Contains(t, err.Error(), "unique suffix not provided in id") - }) - - t.Run("test error from send request", func(t *testing.T) { - serv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusInternalServerError) - })) - defer serv.Close() - - v := sidetree.New(sidetree.WithAuthTokenProvider(&tokenProvider{})) - - pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - signingPubKeyJWK, err := pubkey.GetPublicKeyJWK(pubKey) - require.NoError(t, err) - - rv, err := commitment.GetRevealValue(signingPubKeyJWK, 18) - require.NoError(t, err) - - err = v.DeactivateDID("did:ex:123", - deactivate.WithOperationCommitment(rv), - deactivate.WithSigner(newSignerMock(t, privKey)), deactivate.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - })) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to send deactivate sidetree request") - }) - - t.Run("test error from get reveal value", func(t *testing.T) { - v := sidetree.New(sidetree.WithAuthToken("tk1")) - - _, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - err = v.DeactivateDID("did:ex:123", - deactivate.WithOperationCommitment("value"), - deactivate.WithSigner(newSignerMock(t, privKey)), deactivate.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - })) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to get decoded multihash") - }) - - t.Run("test success", func(t *testing.T) { - serv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - })) - defer serv.Close() - - v := sidetree.New(sidetree.WithAuthToken("tk1")) - - pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - signingPubKeyJWK, err := pubkey.GetPublicKeyJWK(pubKey) - require.NoError(t, err) - - rv, err := commitment.GetRevealValue(signingPubKeyJWK, 18) - require.NoError(t, err) - - err = v.DeactivateDID("did:ex:123", deactivate.WithSigner(newSignerMock(t, privKey)), - deactivate.WithOperationCommitment(rv), deactivate.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{serv.URL}, nil - })) - require.NoError(t, err) - }) -} - -func TestClient_RecoverDID(t *testing.T) { - t.Run("test next recovery key empty", func(t *testing.T) { - v := sidetree.New() - - err := v.RecoverDID("did:ex:123") - require.Error(t, err) - require.Contains(t, err.Error(), "next recovery public key is required") - }) - - t.Run("test next update key empty", func(t *testing.T) { - v := sidetree.New() - - pubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - err = v.RecoverDID("did:ex:123", recovery.WithNextRecoveryPublicKey(pubKey)) - require.Error(t, err) - require.Contains(t, err.Error(), "next update public key is required") - }) - - t.Run("test signing key empty", func(t *testing.T) { - v := sidetree.New() - - pubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - err = v.RecoverDID("did:ex:123", recovery.WithNextRecoveryPublicKey(pubKey), - recovery.WithNextUpdatePublicKey(pubKey)) - require.Error(t, err) - require.Contains(t, err.Error(), "signer is required") - }) - - t.Run("test operation commitment is empty", func(t *testing.T) { - v := sidetree.New() - - pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - err = v.RecoverDID("did:ex:123", recovery.WithNextRecoveryPublicKey(pubKey), - recovery.WithNextUpdatePublicKey(pubKey), recovery.WithSigner(newSignerMock(t, privKey))) - require.Error(t, err) - require.Contains(t, err.Error(), "operation commitment is required") - }) - - t.Run("test error from get endpoints", func(t *testing.T) { - v := sidetree.New() - - pubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - signingKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - signingPubKeyJWK, err := pubkey.GetPublicKeyJWK(&signingKey.PublicKey) - require.NoError(t, err) - - rv, err := commitment.GetRevealValue(signingPubKeyJWK, 18) - require.NoError(t, err) - - err = v.RecoverDID("did:ex:123", - recovery.WithSigner(newSignerMock(t, signingKey)), recovery.WithOperationCommitment(rv), - recovery.WithNextRecoveryPublicKey(pubKey), - recovery.WithNextUpdatePublicKey(pubKey), recovery.WithPublicKey(&doc.PublicKey{ - ID: "key3", - Type: doc.Ed25519VerificationKey2018, - JWK: jwk.JWK{JSONWebKey: gojose.JSONWebKey{Key: pubKey}}, - })) - require.Error(t, err) - require.Contains(t, err.Error(), "sidetree get endpoints func is required") - - i := 0 - - err = v.RecoverDID("did:ex:123", - recovery.WithSigner(newSignerMock(t, signingKey)), recovery.WithOperationCommitment(rv), - recovery.WithNextRecoveryPublicKey(pubKey), - recovery.WithNextUpdatePublicKey(pubKey), recovery.WithPublicKey(&doc.PublicKey{ - ID: "key3", - Type: doc.Ed25519VerificationKey2018, - JWK: jwk.JWK{JSONWebKey: gojose.JSONWebKey{Key: pubKey}}, - }), - recovery.WithSidetreeEndpoint(func(disableCache bool) ([]string, error) { - i++ - - if i == 1 { - require.False(t, disableCache, "expecting disableCache to be false on the first call") - } else { - require.True(t, disableCache, "expecting disableCache to be true on the second call") - } - - return nil, fmt.Errorf("failed to get endpoint") - })) - - require.Error(t, err) - require.Contains(t, err.Error(), "failed to get endpoint") - }) - - t.Run("test failed to get next recovery key", func(t *testing.T) { - v := sidetree.New() - - pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - err = v.RecoverDID("did:ex:123", recovery.WithOperationCommitment("value"), - recovery.WithSigner(newSignerMock(t, privKey)), - recovery.WithNextRecoveryPublicKey([]byte("wrong")), recovery.WithNextUpdatePublicKey(pubKey), - recovery.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - })) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to get next recovery key") - }) - - t.Run("test failed to get next update key", func(t *testing.T) { - v := sidetree.New() - - pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - err = v.RecoverDID("did:ex:123", recovery.WithOperationCommitment("value"), - recovery.WithSigner(newSignerMock(t, privKey)), - recovery.WithNextUpdatePublicKey([]byte("wrong")), recovery.WithNextRecoveryPublicKey(pubKey), - recovery.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - })) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to get next update key") - }) - - t.Run("test error from unique suffix", func(t *testing.T) { - v := sidetree.New() - - pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - err = v.RecoverDID("wrong", recovery.WithOperationCommitment("value"), - recovery.WithSigner(newSignerMock(t, privKey)), - recovery.WithNextUpdatePublicKey(pubKey), recovery.WithNextRecoveryPublicKey(pubKey), - recovery.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - })) - require.Error(t, err) - require.Contains(t, err.Error(), "unique suffix not provided in id") - }) - - t.Run("test error parse public key", func(t *testing.T) { - v := sidetree.New() - - pubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - ecPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - err = v.RecoverDID("did:ex:123", recovery.WithOperationCommitment("value"), - recovery.WithSigner(newSignerMock(t, ecPrivKey)), recovery.WithNextRecoveryPublicKey(pubKey), - recovery.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - }), recovery.WithNextUpdatePublicKey(pubKey), recovery.WithPublicKey(&doc.PublicKey{ - ID: "key3", - Type: doc.JWSVerificationKey2020, - JWK: jwk.JWK{}, - })) - require.Error(t, err) - require.Contains(t, err.Error(), "public key must contain either a jwk or base58 key") - }) - - t.Run("test error from send request", func(t *testing.T) { - serv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusInternalServerError) - })) - defer serv.Close() - - v := sidetree.New(sidetree.WithAuthToken("tk1")) - - pubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - ecPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - signingPubKeyJWK, err := pubkey.GetPublicKeyJWK(&ecPrivKey.PublicKey) - require.NoError(t, err) - - rv, err := commitment.GetRevealValue(signingPubKeyJWK, 18) - require.NoError(t, err) - - err = v.RecoverDID("did:ex:123", recovery.WithOperationCommitment(rv), - recovery.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{serv.URL}, nil - }), recovery.WithSigner(newSignerMock(t, ecPrivKey)), - recovery.WithNextRecoveryPublicKey(pubKey), - recovery.WithNextUpdatePublicKey(pubKey), recovery.WithPublicKey(&doc.PublicKey{ - ID: "key3", - Type: doc.Ed25519VerificationKey2018, - JWK: jwk.JWK{JSONWebKey: gojose.JSONWebKey{Key: pubKey}}, - }), - recovery.WithService(&did.Service{ID: "svc3"})) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to send recover sidetree request") - }) - - t.Run("test error from reveal value", func(t *testing.T) { - v := sidetree.New(sidetree.WithAuthToken("tk1")) - - pubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - ecPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - err = v.RecoverDID("did:ex:123", recovery.WithOperationCommitment("value"), - recovery.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - }), recovery.WithSigner(newSignerMock(t, ecPrivKey)), - recovery.WithNextRecoveryPublicKey(pubKey), - recovery.WithNextUpdatePublicKey(pubKey), recovery.WithPublicKey(&doc.PublicKey{ - ID: "key3", - Type: doc.Ed25519VerificationKey2018, - JWK: jwk.JWK{JSONWebKey: gojose.JSONWebKey{Key: pubKey}}, - }), - recovery.WithService(&did.Service{ID: "svc3"})) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to get decoded multihash") - }) - - t.Run("test success", func(t *testing.T) { - serv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - bytes, err := (&did.Doc{ID: "did1", Context: []string{did.ContextV1}}).JSONBytes() - require.NoError(t, err) - b, err := json.Marshal(didResolution{ - Context: "https://www.w3.org/ns/did-resolution/v1", - DIDDocument: bytes, - }) - require.NoError(t, err) - _, err = fmt.Fprint(w, string(b)) - require.NoError(t, err) - })) - defer serv.Close() - - v := sidetree.New(sidetree.WithAuthToken("tk1")) - - pubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - signingKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - signingPubKeyJWK, err := pubkey.GetPublicKeyJWK(&signingKey.PublicKey) - require.NoError(t, err) - - rv, err := commitment.GetRevealValue(signingPubKeyJWK, 18) - require.NoError(t, err) - - err = v.RecoverDID("did:ex:123", recovery.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{serv.URL}, nil - }), recovery.WithSigner(newSignerMock(t, signingKey)), recovery.WithOperationCommitment(rv), - recovery.WithNextRecoveryPublicKey(pubKey), - recovery.WithNextUpdatePublicKey(pubKey), recovery.WithPublicKey(&doc.PublicKey{ - ID: "key3", - Type: doc.Ed25519VerificationKey2018, - JWK: jwk.JWK{JSONWebKey: gojose.JSONWebKey{Key: pubKey}}, - }), - recovery.WithService(&did.Service{ID: "svc3"}), - recovery.WithAlsoKnownAs("firstIdentityURI"), - recovery.WithAlsoKnownAs("secondIdentityURI")) - require.NoError(t, err) - }) -} - -func TestClient_UpdateDID(t *testing.T) { - t.Run("test signing key empty", func(t *testing.T) { - v := sidetree.New() - - err := v.UpdateDID("did:ex:123") - require.Error(t, err) - require.Contains(t, err.Error(), "signer is required") - }) - - t.Run("test next updates key empty", func(t *testing.T) { - v := sidetree.New() - - _, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - err = v.UpdateDID("did:ex:123", update.WithSigner(newSignerMock(t, privKey))) - require.Error(t, err) - require.Contains(t, err.Error(), "next update public key is required") - }) - - t.Run("operation commitment is empty", func(t *testing.T) { - v := sidetree.New() - - pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - err = v.UpdateDID("did:ex:123", update.WithSigner(newSignerMock(t, privKey)), update.WithNextUpdatePublicKey(pubKey)) - require.Error(t, err) - require.Contains(t, err.Error(), "operation commitment is required") - }) - - t.Run("test error from get endpoints", func(t *testing.T) { - v := sidetree.New() - - pubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - signingKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - signingPubKeyJWK, err := pubkey.GetPublicKeyJWK(&signingKey.PublicKey) - require.NoError(t, err) - - rv, err := commitment.GetRevealValue(signingPubKeyJWK, 18) - require.NoError(t, err) - - err = v.UpdateDID("did:ex:123", - update.WithSigner(newSignerMock(t, signingKey)), - update.WithOperationCommitment(rv), - update.WithNextUpdatePublicKey(pubKey), - update.WithRemoveService("svc1")) - require.Error(t, err) - require.Contains(t, err.Error(), "sidetree get endpoints func is required") - - err = v.UpdateDID("did:ex:123", - update.WithSigner(newSignerMock(t, signingKey)), - update.WithOperationCommitment(rv), - update.WithNextUpdatePublicKey(pubKey), - update.WithSidetreeEndpoint(func(bool) ([]string, error) { - return nil, fmt.Errorf("failed to get endpoints") - }), - update.WithRemoveService("svc1")) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to get endpoints") - }) - - t.Run("test failed to get next update key", func(t *testing.T) { - v := sidetree.New() - - _, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - err = v.UpdateDID("did:ex:123", update.WithOperationCommitment("value"), - update.WithSigner(newSignerMock(t, privKey)), - update.WithNextUpdatePublicKey([]byte("wrong")), update.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - })) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to get next update key") - }) - - t.Run("error from update patches", func(t *testing.T) { - v := sidetree.New() - - pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - defaultOptions := []update.Option{ - update.WithOperationCommitment("value"), - update.WithSigner(newSignerMock(t, privKey)), - update.WithNextUpdatePublicKey(pubKey), - update.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - }), - } - - t.Run("public key error: no jwk in JsonWebKey2020 key", func(t *testing.T) { - err = v.UpdateDID("did:ex:123", append(defaultOptions, - update.WithAddPublicKey(&doc.PublicKey{ - ID: "key3", - Type: doc.JWK2020Type, - B58Key: base58.Encode(pubKey), - }), - )...) - require.Error(t, err) - require.Contains(t, err.Error(), "no valid jwk in JsonWebKey2020 key") - }) - }) - - t.Run("test error from unique suffix", func(t *testing.T) { - v := sidetree.New() - - pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - err = v.UpdateDID("wrong", update.WithOperationCommitment("value"), - update.WithSigner(newSignerMock(t, privKey)), - update.WithNextUpdatePublicKey(pubKey), update.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - })) - require.Error(t, err) - require.Contains(t, err.Error(), "unique suffix not provided in id") - }) - - t.Run("test failed to send update did request", func(t *testing.T) { - serv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusInternalServerError) - })) - defer serv.Close() - - v := sidetree.New(sidetree.WithAuthToken("tk1")) - - pubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - signingKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - signingPubKeyJWK, err := pubkey.GetPublicKeyJWK(&signingKey.PublicKey) - require.NoError(t, err) - - rv, err := commitment.GetRevealValue(signingPubKeyJWK, 18) - require.NoError(t, err) - - err = v.UpdateDID("did:ex:123", update.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{serv.URL}, nil - }), update.WithSigner(newSignerMock(t, signingKey)), update.WithOperationCommitment(rv), - update.WithNextUpdatePublicKey(pubKey), update.WithRemoveService("svc1"), - update.WithRemoveService("svc1"), update.WithRemovePublicKey("k1"), - update.WithRemovePublicKey("k2"), update.WithAddPublicKey(&doc.PublicKey{ - ID: "key3", - Type: doc.Ed25519VerificationKey2018, - JWK: jwk.JWK{JSONWebKey: gojose.JSONWebKey{Key: pubKey}}, - }), - update.WithAddService(&did.Service{ID: "svc3"})) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to send update did request") - }) - - t.Run("test failed from reveal value", func(t *testing.T) { - v := sidetree.New(sidetree.WithAuthToken("tk1")) - - pubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - signingKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - err = v.UpdateDID("did:ex:123", update.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - }), update.WithSigner(newSignerMock(t, signingKey)), update.WithOperationCommitment("value"), - update.WithNextUpdatePublicKey(pubKey), update.WithRemoveService("svc1"), - update.WithRemoveService("svc1"), update.WithRemovePublicKey("k1"), - update.WithRemovePublicKey("k2"), update.WithAddPublicKey(&doc.PublicKey{ - ID: "key3", - Type: doc.Ed25519VerificationKey2018, - JWK: jwk.JWK{JSONWebKey: gojose.JSONWebKey{Key: pubKey}}, - }), - update.WithAddService(&did.Service{ID: "svc3"})) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to get decoded multihash") - }) - - t.Run("test success", func(t *testing.T) { - serv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - })) - defer serv.Close() - - v := sidetree.New(sidetree.WithAuthToken("tk1")) - - pubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - signingKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - signingPubKeyJWK, err := pubkey.GetPublicKeyJWK(&signingKey.PublicKey) - require.NoError(t, err) - - rv, err := commitment.GetRevealValue(signingPubKeyJWK, 18) - require.NoError(t, err) - - err = v.UpdateDID("did:ex:123", update.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{serv.URL}, nil - }), update.WithSigner(newSignerMock(t, signingKey)), update.WithOperationCommitment(rv), - update.WithNextUpdatePublicKey(pubKey), update.WithRemoveService("svc1"), - update.WithRemoveService("svc1"), update.WithRemovePublicKey("k1"), - update.WithRemovePublicKey("k2"), update.WithAddPublicKey(&doc.PublicKey{ - ID: "key3", - Type: doc.Ed25519VerificationKey2018, - B58Key: base58.Encode(pubKey), - }), - update.WithAddService(&did.Service{ID: "svc3"}), - update.WithAddAlsoKnownAs("firstIdentityURI"), - update.WithAddAlsoKnownAs("secondIdentityURI"), - update.WithRemoveAlsoKnownAs("removeIdentityURI")) - require.NoError(t, err) - }) -} - -func TestClient_CreateDID(t *testing.T) { - t.Run("test error from get endpoints", func(t *testing.T) { - v := sidetree.New() - - ed25519RecoveryPubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - ecUpdatePrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - didResol, err := v.CreateDID(create.WithRecoveryPublicKey(ed25519RecoveryPubKey), - create.WithUpdatePublicKey(ecUpdatePrivKey.Public()), - create.WithPublicKey(&doc.PublicKey{ - ID: "key1", - Type: doc.JWSVerificationKey2020, - JWK: jwk.JWK{JSONWebKey: gojose.JSONWebKey{Key: ed25519RecoveryPubKey}}, - Purposes: []string{doc.KeyPurposeAuthentication}, - })) - - require.Contains(t, err.Error(), "sidetree get endpoints func is required") - require.Nil(t, didResol) - - didResol, err = v.CreateDID(create.WithRecoveryPublicKey(ed25519RecoveryPubKey), - create.WithUpdatePublicKey(ecUpdatePrivKey.Public()), - create.WithPublicKey(&doc.PublicKey{ - ID: "key1", - Type: doc.JWSVerificationKey2020, - JWK: jwk.JWK{JSONWebKey: gojose.JSONWebKey{Key: ed25519RecoveryPubKey}}, - Purposes: []string{doc.KeyPurposeAuthentication}, - }), - create.WithSidetreeEndpoint(func(bool) ([]string, error) { - return nil, fmt.Errorf("failed to get endpoints") - })) - - require.Error(t, err) - require.Contains(t, err.Error(), "failed to get endpoints") - require.Nil(t, didResol) - }) - - t.Run("test error from send create sidetree request", func(t *testing.T) { - v := sidetree.New() - - ed25519RecoveryPubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - ed25519UpdatePubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - didResol, err := v.CreateDID(create.WithRecoveryPublicKey(ed25519RecoveryPubKey), - create.WithUpdatePublicKey(ed25519UpdatePubKey), create.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - })) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to send request") - require.Nil(t, didResol) - - // test http status not equal 200 - serv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusInternalServerError) - })) - defer serv.Close() - - didResol, err = v.CreateDID(create.WithRecoveryPublicKey(ed25519RecoveryPubKey), - create.WithUpdatePublicKey(ed25519UpdatePubKey), create.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{serv.URL}, nil - })) - require.Error(t, err) - require.Contains(t, err.Error(), "got unexpected response") - require.Nil(t, didResol) - - // test failed to parse did - serv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - bytes, err1 := (&did.Doc{ID: "did1"}).JSONBytes() - require.NoError(t, err1) - _, err1 = fmt.Fprint(w, string(bytes)) - require.NoError(t, err1) - })) - defer serv.Close() - - didResol, err = v.CreateDID(create.WithRecoveryPublicKey(ed25519RecoveryPubKey), - create.WithUpdatePublicKey(ed25519UpdatePubKey), create.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{serv.URL}, nil - })) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to parse did document") - require.Nil(t, didResol) - }) - - t.Run("test error from sidetree operation request function", func(t *testing.T) { - v := sidetree.New(sidetree.WithSidetreeOperationRequestFnc( - func(req []byte, getEndpoints func(bool) ([]string, error)) ([]byte, error) { - return nil, fmt.Errorf("send operation request error") - })) - - ed25519RecoveryPubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - ed25519UpdatePubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - didResol, err := v.CreateDID(create.WithRecoveryPublicKey(ed25519RecoveryPubKey), - create.WithUpdatePublicKey(ed25519UpdatePubKey), create.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"https://www.domain.com"}, nil - })) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to send create sidetree request: send operation request error") - require.Nil(t, didResol) - }) - - t.Run("test success", func(t *testing.T) { - serv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - bytes, err := (&did.Doc{ID: "did1", Context: []string{did.ContextV1}}).JSONBytes() - require.NoError(t, err) - b, err := json.Marshal(didResolution{ - Context: "https://www.w3.org/ns/did-resolution/v1", - DIDDocument: bytes, - }) - require.NoError(t, err) - _, err = fmt.Fprint(w, string(b)) - require.NoError(t, err) - })) - defer serv.Close() - - ed25519RecoveryPubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - ecUpdatePrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - ecPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - v := sidetree.New(sidetree.WithHTTPClient(&http.Client{})) - - didResol, err := v.CreateDID(create.WithRecoveryPublicKey(ed25519RecoveryPubKey), - create.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{serv.URL}, nil - }), create.WithUpdatePublicKey(ecUpdatePrivKey.Public()), - create.WithAlsoKnownAs("https://first.blog.example"), - create.WithAlsoKnownAs("https://second.blog.example"), - create.WithPublicKey(&doc.PublicKey{ - ID: "key1", - Type: doc.JWSVerificationKey2020, - JWK: jwk.JWK{JSONWebKey: gojose.JSONWebKey{Key: ed25519RecoveryPubKey}}, - Purposes: []string{doc.KeyPurposeAuthentication}, - }), - create.WithPublicKey(&doc.PublicKey{ - ID: "key2", - Type: doc.JWSVerificationKey2020, - JWK: jwk.JWK{JSONWebKey: gojose.JSONWebKey{Key: ecPrivKey.Public()}}, - Purposes: []string{doc.KeyPurposeAuthentication}, - }), - create.WithService(&did.Service{ - ID: "srv1", - Type: "type", - ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{ - { - URI: "http://example.com", - RoutingKeys: []string{"key1"}, - }, - }), - Properties: map[string]interface{}{"priority": "1"}, - })) - require.NoError(t, err) - require.Equal(t, "did1", didResol.DIDDocument.ID) - }) - - t.Run("test error unmarshal result", func(t *testing.T) { - serv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, err := fmt.Fprint(w, "{{") - require.NoError(t, err) - })) - defer serv.Close() - - ed25519RecoveryPubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - ecUpdatePrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - v := sidetree.New(sidetree.WithHTTPClient(&http.Client{})) - - _, err = v.CreateDID(create.WithRecoveryPublicKey(ed25519RecoveryPubKey), - create.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{serv.URL}, nil - }), create.WithUpdatePublicKey(ecUpdatePrivKey.Public()), - create.WithService(&did.Service{ - ID: "srv1", - Type: "type", - ServiceEndpoint: model.NewDIDCommV1Endpoint("http://example.com"), - Properties: map[string]interface{}{"priority": "1"}, - })) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to parse document resolution") - }) - - t.Run("test unsupported recovery public key type", func(t *testing.T) { - serv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - bytes, err := (&did.Doc{ID: "did1", Context: []string{did.ContextV1}}).JSONBytes() - require.NoError(t, err) - _, err = fmt.Fprint(w, string(bytes)) - require.NoError(t, err) - })) - defer serv.Close() - - v := sidetree.New() - - didResol, err := v.CreateDID(create.WithRecoveryPublicKey("wrongkey"), - create.WithUpdatePublicKey("wrongvalue"), - create.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - })) - require.Error(t, err) - require.Contains(t, err.Error(), "failed to get recovery key") - require.Nil(t, didResol) - }) - - t.Run("test recovery public key empty", func(t *testing.T) { - serv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - bytes, err := (&did.Doc{ID: "did1", Context: []string{did.ContextV1}}).JSONBytes() - require.NoError(t, err) - _, err = fmt.Fprint(w, string(bytes)) - require.NoError(t, err) - })) - defer serv.Close() - - v := sidetree.New() - - didResol, err := v.CreateDID() - require.Error(t, err) - require.Contains(t, err.Error(), "recovery public key is required") - require.Nil(t, didResol) - }) - - t.Run("test update public key empty", func(t *testing.T) { - serv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - bytes, err := (&did.Doc{ID: "did1", Context: []string{did.ContextV1}}).JSONBytes() - require.NoError(t, err) - _, err = fmt.Fprint(w, string(bytes)) - require.NoError(t, err) - })) - defer serv.Close() - - pubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - v := sidetree.New() - - didResol, err := v.CreateDID(create.WithRecoveryPublicKey(pubKey), - create.WithSidetreeEndpoint(func(bool) ([]string, error) { - return []string{"url"}, nil - })) - require.Error(t, err) - require.Contains(t, err.Error(), "update public key is required") - require.Nil(t, didResol) - }) -} - -type signerMock struct { - signer client.Signer - publicKey *jws.JWK -} - -func newSignerMock(t *testing.T, signingkey crypto.PrivateKey) *signerMock { - t.Helper() - - switch key := signingkey.(type) { - case *ecdsa.PrivateKey: - updateKey, err := pubkey.GetPublicKeyJWK(key.Public()) - require.NoError(t, err) - - return &signerMock{signer: ecsigner.New(key, "ES256", "k1"), publicKey: updateKey} - case ed25519.PrivateKey: - updateKey, err := pubkey.GetPublicKeyJWK(key.Public()) - require.NoError(t, err) - - return &signerMock{signer: edsigner.New(key, "EdDSA", "k1"), publicKey: updateKey} - } - - return nil -} - -func (s *signerMock) Sign(data []byte) ([]byte, error) { - return s.signer.Sign(data) -} - -func (s *signerMock) Headers() jws.Headers { - return s.signer.Headers() -} - -func (s *signerMock) PublicKeyJWK() *jws.JWK { - return s.publicKey -} - -type tokenProvider struct{} - -func (t *tokenProvider) AuthToken() (string, error) { - return "newTK", nil -} diff --git a/method/sidetreelongform/sidetree/doc/doc.go b/method/sidetreelongform/sidetree/doc/doc.go deleted file mode 100644 index 9eb0d0a..0000000 --- a/method/sidetreelongform/sidetree/doc/doc.go +++ /dev/null @@ -1,199 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -// Package doc implements sidetree document -package doc - -import ( - "bytes" - "encoding/json" - "fmt" - - "github.com/trustbloc/kms-go/doc/jose/jwk" - - docdid "github.com/trustbloc/did-go/doc/did" -) - -const ( - jsonldID = "id" - jsonldType = "type" - jsonldPurposes = "purposes" - jsonldServicePoint = "serviceEndpoint" - jsonldRecipientKeys = "recipientKeys" - jsonldRoutingKeys = "routingKeys" - jsonldPriority = "priority" - jsonldAccept = "accept" - - jsonldPublicKeyJwk = "publicKeyJwk" - jsonldPublicKeyBase58 = "publicKeyBase58" - - // KeyPurposeAuthentication defines key purpose as authentication key. - KeyPurposeAuthentication = "authentication" - // KeyPurposeAssertionMethod defines key purpose as assertion key. - KeyPurposeAssertionMethod = "assertionMethod" - // KeyPurposeKeyAgreement defines key purpose as agreement key. - KeyPurposeKeyAgreement = "keyAgreement" - // KeyPurposeCapabilityDelegation defines key purpose as delegation key. - KeyPurposeCapabilityDelegation = "capabilityDelegation" - // KeyPurposeCapabilityInvocation defines key purpose as invocation key. - KeyPurposeCapabilityInvocation = "capabilityInvocation" - - // JWSVerificationKey2020 defines key type signature. - JWSVerificationKey2020 = "JwsVerificationKey2020" - - // JWK2020Type defines key type for JWK public keys. - JWK2020Type = "JsonWebKey2020" - - // Ed25519VerificationKey2018 define key type signature. - Ed25519VerificationKey2018 = "Ed25519VerificationKey2018" -) - -type rawDoc struct { - PublicKey []map[string]interface{} `json:"publicKey,omitempty"` - Service []map[string]interface{} `json:"service,omitempty"` - AlsoKnownAs []interface{} `json:"alsoKnownAs,omitempty"` -} - -// Doc DID Document definition. -type Doc struct { - PublicKey []PublicKey - Service []docdid.Service - AlsoKnownAs []string -} - -// PublicKey struct. -type PublicKey struct { - ID string - Type string - Purposes []string - JWK jwk.JWK - B58Key string -} - -// JSONBytes converts document to json bytes. -func (doc *Doc) JSONBytes() ([]byte, error) { - publicKeys, err := PopulateRawPublicKeys(doc.PublicKey) - if err != nil { - return nil, fmt.Errorf("JSON unmarshalling of Public Key failed: %w", err) - } - - services, err := PopulateRawServices(doc.Service) - if err != nil { - return nil, err - } - - alsoKnownAs := PopulateRawAlsoKnownAs(doc.AlsoKnownAs) - - raw := &rawDoc{ - PublicKey: publicKeys, - Service: services, - AlsoKnownAs: alsoKnownAs, - } - - byteDoc, err := json.Marshal(raw) - if err != nil { - return nil, fmt.Errorf("JSON marshalling of document failed: %w", err) - } - - return byteDoc, nil -} - -// PopulateRawPublicKeys populate raw public keys. -func PopulateRawPublicKeys(pks []PublicKey) ([]map[string]interface{}, error) { - rawPKs := make([]map[string]interface{}, 0) - - for i := range pks { - publicKey, err := populateRawPublicKey(&pks[i]) - if err != nil { - return nil, err - } - - rawPKs = append(rawPKs, publicKey) - } - - return rawPKs, nil -} - -func populateRawPublicKey(pk *PublicKey) (map[string]interface{}, error) { - rawPK := make(map[string]interface{}) - rawPK[jsonldID] = pk.ID - rawPK[jsonldType] = pk.Type - rawPK[jsonldPurposes] = pk.Purposes - - jwkBytes, err := pk.JWK.MarshalJSON() - - switch { - case err == nil: - rawJWK := make(map[string]interface{}) - if err := json.Unmarshal(jwkBytes, &rawJWK); err != nil { - return nil, err - } - - rawPK[jsonldPublicKeyJwk] = rawJWK - case pk.Type == JWK2020Type: - return nil, fmt.Errorf("no valid jwk in JsonWebKey2020 key") - case pk.B58Key != "": - rawPK[jsonldPublicKeyBase58] = pk.B58Key - default: - return nil, fmt.Errorf("public key must contain either a jwk or base58 key") - } - - return rawPK, nil -} - -// PopulateRawServices populate raw services. -func PopulateRawServices(services []docdid.Service) ([]map[string]interface{}, error) { - rawServices := make([]map[string]interface{}, 0) - - for i := range services { - rawService := make(map[string]interface{}) - - for k, v := range services[i].Properties { - rawService[k] = v - } - - rawService[jsonldID] = services[i].ID - rawService[jsonldType] = services[i].Type - - serviceEndpoint, err := services[i].ServiceEndpoint.MarshalJSON() - if err != nil { - return nil, err - } - - if !bytes.Equal(serviceEndpoint, []byte("null")) { - rawService[jsonldServicePoint] = json.RawMessage(serviceEndpoint) - } - - if services[i].Priority != nil { - rawService[jsonldPriority] = services[i].Priority - } - - if len(services[i].RecipientKeys) > 0 { - rawService[jsonldRecipientKeys] = services[i].RecipientKeys - } - - if len(services[i].RoutingKeys) > 0 { - rawService[jsonldRoutingKeys] = services[i].RoutingKeys - } - - if len(services[i].Accept) > 0 { - rawService[jsonldAccept] = services[i].Accept - } - - rawServices = append(rawServices, rawService) - } - - return rawServices, nil -} - -// PopulateRawAlsoKnownAs populates raw also known as. -func PopulateRawAlsoKnownAs(alsoKnownAs []string) []interface{} { - values := make([]interface{}, len(alsoKnownAs)) - for i, v := range alsoKnownAs { - values[i] = v - } - - return values -} diff --git a/method/sidetreelongform/sidetree/option.go b/method/sidetreelongform/sidetree/option.go deleted file mode 100644 index 41da0f1..0000000 --- a/method/sidetreelongform/sidetree/option.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -package sidetree - -import ( - "net/http" -) - -// Option is a DID client instance option. -type Option func(opts *Client) - -// WithHTTPClient option is for custom http client. -func WithHTTPClient(httpClient *http.Client) Option { - return func(opts *Client) { - opts.client = httpClient - } -} - -// WithAuthToken add auth token. -func WithAuthToken(authToken string) Option { - return func(opts *Client) { - opts.authToken = "Bearer " + authToken - } -} - -// WithAuthTokenProvider add auth token provider. -func WithAuthTokenProvider(p authTokenProvider) Option { - return func(opts *Client) { - opts.authTokenProvider = p - } -} - -// WithSidetreeOperationRequestFnc overrides default sidetree operation request. -func WithSidetreeOperationRequestFnc(fnc func(req []byte, getEndpoints GetEndpointsFunc) ([]byte, error)) Option { - return func(opts *Client) { - opts.sendRequest = fnc - } -} diff --git a/method/sidetreelongform/sidetree/option/create/option.go b/method/sidetreelongform/sidetree/option/create/option.go deleted file mode 100644 index bcefc87..0000000 --- a/method/sidetreelongform/sidetree/option/create/option.go +++ /dev/null @@ -1,87 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -// Package create implements sidetree create did option -package create - -import ( - "crypto" - - docdid "github.com/trustbloc/did-go/doc/did" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/doc" -) - -// Opts create did opts. -type Opts struct { - PublicKeys []doc.PublicKey - Services []docdid.Service - AlsoKnownAs []string - GetEndpoints func(disableCache bool) ([]string, error) - RecoveryPublicKey crypto.PublicKey - UpdatePublicKey crypto.PublicKey - SigningKey crypto.PrivateKey - SigningKeyID string - MultiHashAlgorithm uint - AnchorOrigin string -} - -// Option is a create DID option. -type Option func(opts *Opts) - -// WithPublicKey add DID public key. -func WithPublicKey(publicKey *doc.PublicKey) Option { - return func(opts *Opts) { - opts.PublicKeys = append(opts.PublicKeys, *publicKey) - } -} - -// WithService add service. -func WithService(service *docdid.Service) Option { - return func(opts *Opts) { - opts.Services = append(opts.Services, *service) - } -} - -// WithAlsoKnownAs adds also known as URI. -func WithAlsoKnownAs(uri string) Option { - return func(opts *Opts) { - opts.AlsoKnownAs = append(opts.AlsoKnownAs, uri) - } -} - -// WithSidetreeEndpoint get sidetree endpoints. -func WithSidetreeEndpoint(getEndpoints func(disableCache bool) ([]string, error)) Option { - return func(opts *Opts) { - opts.GetEndpoints = getEndpoints - } -} - -// WithRecoveryPublicKey set recovery public key. -func WithRecoveryPublicKey(recoveryPublicKey crypto.PublicKey) Option { - return func(opts *Opts) { - opts.RecoveryPublicKey = recoveryPublicKey - } -} - -// WithUpdatePublicKey set update public key. -func WithUpdatePublicKey(updatePublicKey crypto.PublicKey) Option { - return func(opts *Opts) { - opts.UpdatePublicKey = updatePublicKey - } -} - -// WithMultiHashAlgorithm set multi hash algorithm for sidetree request. -func WithMultiHashAlgorithm(multiHashAlgorithm uint) Option { - return func(opts *Opts) { - opts.MultiHashAlgorithm = multiHashAlgorithm - } -} - -// WithAnchorOrigin set anchor origin for sidetree request. -func WithAnchorOrigin(anchorOrigin string) Option { - return func(opts *Opts) { - opts.AnchorOrigin = anchorOrigin - } -} diff --git a/method/sidetreelongform/sidetree/option/deactivate/option.go b/method/sidetreelongform/sidetree/option/deactivate/option.go deleted file mode 100644 index 7c3972b..0000000 --- a/method/sidetreelongform/sidetree/option/deactivate/option.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -// Package deactivate implements sidetree deactivate did option -package deactivate - -import ( - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/api" -) - -// Opts deactivate did opts. -type Opts struct { - GetEndpoints func(disableCache bool) ([]string, error) - Signer api.Signer - OperationCommitment string -} - -// Option is a deactivate DID option. -type Option func(opts *Opts) - -// WithSidetreeEndpoint get sidetree endpoints. -func WithSidetreeEndpoint(getEndpoints func(disableCache bool) ([]string, error)) Option { - return func(opts *Opts) { - opts.GetEndpoints = getEndpoints - } -} - -// WithSigner set signer. -func WithSigner(signer api.Signer) Option { - return func(opts *Opts) { - opts.Signer = signer - } -} - -// WithOperationCommitment sets last operation commitment. -func WithOperationCommitment(operationCommitment string) Option { - return func(opts *Opts) { - opts.OperationCommitment = operationCommitment - } -} diff --git a/method/sidetreelongform/sidetree/option/recovery/option.go b/method/sidetreelongform/sidetree/option/recovery/option.go deleted file mode 100644 index 389209a..0000000 --- a/method/sidetreelongform/sidetree/option/recovery/option.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -// Package recovery implements sidetree recovery did option -package recovery - -import ( - "crypto" - - docdid "github.com/trustbloc/did-go/doc/did" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/api" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/doc" -) - -// Opts recover did opts. -type Opts struct { - PublicKeys []doc.PublicKey - Services []docdid.Service - AlsoKnownAs []string - GetEndpoints func(disableCache bool) ([]string, error) - NextRecoveryPublicKey crypto.PublicKey - NextUpdatePublicKey crypto.PublicKey - Signer api.Signer - OperationCommitment string - MultiHashAlgorithm uint - AnchorOrigin string -} - -// Option is a recover DID option. -type Option func(opts *Opts) - -// WithPublicKey add DID public key. -func WithPublicKey(publicKey *doc.PublicKey) Option { - return func(opts *Opts) { - opts.PublicKeys = append(opts.PublicKeys, *publicKey) - } -} - -// WithService add service. -func WithService(service *docdid.Service) Option { - return func(opts *Opts) { - opts.Services = append(opts.Services, *service) - } -} - -// WithAlsoKnownAs adds also known as URI. -func WithAlsoKnownAs(uri string) Option { - return func(opts *Opts) { - opts.AlsoKnownAs = append(opts.AlsoKnownAs, uri) - } -} - -// WithSidetreeEndpoint get sidetree endpoints. -func WithSidetreeEndpoint(getEndpoints func(disableCache bool) ([]string, error)) Option { - return func(opts *Opts) { - opts.GetEndpoints = getEndpoints - } -} - -// WithNextRecoveryPublicKey set next recovery public key. -func WithNextRecoveryPublicKey(nextRecoveryPublicKey crypto.PublicKey) Option { - return func(opts *Opts) { - opts.NextRecoveryPublicKey = nextRecoveryPublicKey - } -} - -// WithNextUpdatePublicKey set next update public key. -func WithNextUpdatePublicKey(nextUpdatePublicKey crypto.PublicKey) Option { - return func(opts *Opts) { - opts.NextUpdatePublicKey = nextUpdatePublicKey - } -} - -// WithSigner set signer. -func WithSigner(signer api.Signer) Option { - return func(opts *Opts) { - opts.Signer = signer - } -} - -// WithMultiHashAlgorithm set multi hash algorithm for sidetree request. -func WithMultiHashAlgorithm(multiHashAlgorithm uint) Option { - return func(opts *Opts) { - opts.MultiHashAlgorithm = multiHashAlgorithm - } -} - -// WithOperationCommitment sets last operation commitment. -func WithOperationCommitment(operationCommitment string) Option { - return func(opts *Opts) { - opts.OperationCommitment = operationCommitment - } -} - -// WithAnchorOrigin set anchor origin for sidetree request. -func WithAnchorOrigin(anchorOrigin string) Option { - return func(opts *Opts) { - opts.AnchorOrigin = anchorOrigin - } -} diff --git a/method/sidetreelongform/sidetree/option/update/option.go b/method/sidetreelongform/sidetree/option/update/option.go deleted file mode 100644 index 50d0ea0..0000000 --- a/method/sidetreelongform/sidetree/option/update/option.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -// Package update implements sidetree update did option -package update - -import ( - "crypto" - - docdid "github.com/trustbloc/did-go/doc/did" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/api" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/doc" -) - -// Option is a update DID option. -type Option func(opts *Opts) - -// Opts update did opts. -type Opts struct { - AddPublicKeys []doc.PublicKey - AddServices []docdid.Service - AddAlsoKnownAs []string - RemovePublicKeys []string - RemoveServices []string - RemoveAlsoKnownAs []string - GetEndpoints func(disableCache bool) ([]string, error) - NextUpdatePublicKey crypto.PublicKey - Signer api.Signer - OperationCommitment string - MultiHashAlgorithm uint -} - -// WithAddPublicKey add DID public key. -func WithAddPublicKey(publicKey *doc.PublicKey) Option { - return func(opts *Opts) { - opts.AddPublicKeys = append(opts.AddPublicKeys, *publicKey) - } -} - -// WithAddService set services to be added. -func WithAddService(service *docdid.Service) Option { - return func(opts *Opts) { - opts.AddServices = append(opts.AddServices, *service) - } -} - -// WithAddAlsoKnownAs adds also known as. -func WithAddAlsoKnownAs(uri string) Option { - return func(opts *Opts) { - opts.AddAlsoKnownAs = append(opts.AddAlsoKnownAs, uri) - } -} - -// WithRemoveAlsoKnownAs removes also known as. -func WithRemoveAlsoKnownAs(uri string) Option { - return func(opts *Opts) { - opts.RemoveAlsoKnownAs = append(opts.RemoveAlsoKnownAs, uri) - } -} - -// WithRemovePublicKey set remove public key id. -func WithRemovePublicKey(publicKeyID string) Option { - return func(opts *Opts) { - opts.RemovePublicKeys = append(opts.RemovePublicKeys, publicKeyID) - } -} - -// WithSigner set signer. -func WithSigner(signer api.Signer) Option { - return func(opts *Opts) { - opts.Signer = signer - } -} - -// WithRemoveService set remove service id. -func WithRemoveService(serviceID string) Option { - return func(opts *Opts) { - opts.RemoveServices = append(opts.RemoveServices, serviceID) - } -} - -// WithNextUpdatePublicKey set next update public key. -func WithNextUpdatePublicKey(nextUpdatePublicKey crypto.PublicKey) Option { - return func(opts *Opts) { - opts.NextUpdatePublicKey = nextUpdatePublicKey - } -} - -// WithSidetreeEndpoint get sidetree endpoints. -func WithSidetreeEndpoint(getEndpoints func(disableCache bool) ([]string, error)) Option { - return func(opts *Opts) { - opts.GetEndpoints = getEndpoints - } -} - -// WithOperationCommitment sets last operation commitment. -func WithOperationCommitment(operationCommitment string) Option { - return func(opts *Opts) { - opts.OperationCommitment = operationCommitment - } -} - -// WithMultiHashAlgorithm set multi hash algorithm for sidetree request. -func WithMultiHashAlgorithm(multiHashAlgorithm uint) Option { - return func(opts *Opts) { - opts.MultiHashAlgorithm = multiHashAlgorithm - } -} diff --git a/method/sidetreelongform/vdr.go b/method/sidetreelongform/vdr.go deleted file mode 100644 index 445cc71..0000000 --- a/method/sidetreelongform/vdr.go +++ /dev/null @@ -1,386 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -// Package sidetreelongform implement long-form vdr -package sidetreelongform - -import ( - "crypto" - "crypto/ed25519" - "crypto/rand" - "encoding/json" - "fmt" - "strings" - - "github.com/btcsuite/btcutil/base58" - jsonld "github.com/piprate/json-gold/ld" - "github.com/trustbloc/sidetree-go/pkg/document" - - ld "github.com/trustbloc/did-go/doc/ld/documentloader" - ldstore "github.com/trustbloc/did-go/doc/ld/store" - - docdid "github.com/trustbloc/did-go/doc/did" - "github.com/trustbloc/did-go/legacy/mem" - "github.com/trustbloc/did-go/method/sidetreelongform/dochandler" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/doc" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/option/create" - vdrapi "github.com/trustbloc/did-go/vdr/api" -) - -const ( - // UpdatePublicKeyOpt update public key opt. - UpdatePublicKeyOpt = "updatePublicKey" - // RecoveryPublicKeyOpt recovery public key opt. - RecoveryPublicKeyOpt = "recoveryPublicKey" - - sha2_256 = 18 - defaultDIDMethod = "ion" - - // DIDAcceptOpt is DID accept option. - DIDAcceptOpt = "didAcceptOpt" - - // VDRAcceptOpt is VDR accept option. - VDRAcceptOpt = "vdrAcceptOpt" -) - -type sidetreeClient interface { - CreateDID(opts ...create.Option) (*docdid.DocResolution, error) -} - -// VDR bloc. -type VDR struct { - method string - - sidetreeDocHandler sidetreeDocumentHandler - sidetreeClient sidetreeClient - - documentLoader jsonld.DocumentLoader -} - -type sidetreeDocumentHandler interface { - ResolveDocument(longFormDID string, opts ...document.ResolutionOption) (*document.ResolutionResult, error) - ProcessOperation(operationBuffer []byte) (*document.ResolutionResult, error) -} - -// New creates new long form VDR. -func New(opts ...Option) (*VDR, error) { - v := &VDR{method: defaultDIDMethod} - - for _, opt := range opts { - opt(v) - } - - if v.documentLoader == nil { - l, err := createJSONLDDocumentLoader() - if err != nil { - return nil, fmt.Errorf("failed to init default jsonld document loader: %w", err) - } - - v.documentLoader = l - } - - var err error - - v.sidetreeDocHandler, err = dochandler.New(fmt.Sprintf("did:%s", v.method)) - if err != nil { - return nil, err - } - - v.sidetreeClient = sidetree.New(sidetree.WithSidetreeOperationRequestFnc(v.sendRequest)) - - return v, nil -} - -type ldStoreProvider struct { - ContextStore ldstore.ContextStore - RemoteProviderStore ldstore.RemoteProviderStore -} - -func (p *ldStoreProvider) JSONLDContextStore() ldstore.ContextStore { - return p.ContextStore -} - -func (p *ldStoreProvider) JSONLDRemoteProviderStore() ldstore.RemoteProviderStore { - return p.RemoteProviderStore -} - -func createJSONLDDocumentLoader() (jsonld.DocumentLoader, error) { - contextStore, err := ldstore.NewContextStore(mem.NewProvider()) - if err != nil { - return nil, fmt.Errorf("create JSON-LD context store: %w", err) - } - - remoteProviderStore, err := ldstore.NewRemoteProviderStore(mem.NewProvider()) - if err != nil { - return nil, fmt.Errorf("create remote provider store: %w", err) - } - - ldStore := &ldStoreProvider{ - ContextStore: contextStore, - RemoteProviderStore: remoteProviderStore, - } - - documentLoader, err := ld.NewDocumentLoader(ldStore) - if err != nil { - return nil, fmt.Errorf("new document loader: %w", err) - } - - return documentLoader, nil -} - -// Accept did method. -func (v *VDR) Accept(method string, opts ...vdrapi.DIDMethodOption) bool { - if method != v.method { - return false - } - - // method is valid; now check additional info (if present) - acceptOpts := &vdrapi.DIDMethodOpts{Values: make(map[string]interface{})} - - // apply options - for _, opt := range opts { - opt(acceptOpts) - } - - // vdr accept option can be provided as additional clue for choosing (or rejecting) VDR - vdrAcceptObj, ok := acceptOpts.Values[VDRAcceptOpt] - if ok { - vdrAccept, strOk := vdrAcceptObj.(string) - if strOk { - return vdrAccept == "long-form" - } - } - - // DID accept option is always provided by registry for read, update and deactivate. - didObj, ok := acceptOpts.Values[DIDAcceptOpt] - if ok { - did, strOk := didObj.(string) - if strOk { - didParts := strings.Split(did, ":") - return len(didParts) > 3 - } - } - - return false -} - -// Close vdr. -func (v *VDR) Close() error { - return nil -} - -// Create did doc. -// -//nolint:gocyclo,funlen -func (v *VDR) Create(did *docdid.Doc, - opts ...vdrapi.DIDMethodOption) (*docdid.DocResolution, error) { - didMethodOpts := &vdrapi.DIDMethodOpts{Values: make(map[string]interface{})} - - // Apply options - for _, opt := range opts { - opt(didMethodOpts) - } - - createOpt := make([]create.Option, 0) - - // get keys - if didMethodOpts.Values[UpdatePublicKeyOpt] == nil { - updateKey, _, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - return nil, fmt.Errorf("creating default update key: %w", err) - } - - didMethodOpts.Values[UpdatePublicKeyOpt] = updateKey - } - - updatePublicKey, ok := didMethodOpts.Values[UpdatePublicKeyOpt].(crypto.PublicKey) - if !ok { - return nil, fmt.Errorf("upatePublicKey is not crypto.PublicKey") - } - - if didMethodOpts.Values[RecoveryPublicKeyOpt] == nil { - recoveryKey, _, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - return nil, fmt.Errorf("creating default recovery key: %w", err) - } - - didMethodOpts.Values[RecoveryPublicKeyOpt] = recoveryKey - } - - recoveryPublicKey, ok := didMethodOpts.Values[RecoveryPublicKeyOpt].(crypto.PublicKey) - if !ok { - return nil, fmt.Errorf("recoveryPublicKey is not crypto.PublicKey") - } - - // get also known as - for i := range did.AlsoKnownAs { - createOpt = append(createOpt, create.WithAlsoKnownAs(did.AlsoKnownAs[i])) - } - - // get services - for i := range did.Service { - createOpt = append(createOpt, create.WithService(&did.Service[i])) - } - - // get verification method - pks, err := getSidetreePublicKeys(did) - if err != nil { - return nil, err - } - - for k := range pks { - createOpt = append(createOpt, create.WithPublicKey(pks[k].publicKey)) - } - - createOpt = append(createOpt, - create.WithMultiHashAlgorithm(sha2_256), - create.WithUpdatePublicKey(updatePublicKey), - create.WithRecoveryPublicKey(recoveryPublicKey)) - - createdDID, err := v.sidetreeClient.CreateDID(createOpt...) - if err != nil { - return nil, err - } - - return createdDID, nil -} - -// Read long-form DID. -func (v *VDR) Read(longFormDID string, _ ...vdrapi.DIDMethodOption) (*docdid.DocResolution, error) { - resolutionResult, err := v.sidetreeDocHandler.ResolveDocument(longFormDID) - if err != nil { - return nil, err - } - - resolutionResultBytes, err := json.Marshal(resolutionResult) - if err != nil { - return nil, err - } - - documentResolution, err := docdid.ParseDocumentResolution(resolutionResultBytes) - if err != nil { - return nil, err - } - - return &docdid.DocResolution{ - DIDDocument: documentResolution.DIDDocument, - DocumentMetadata: documentResolution.DocumentMetadata, - Context: documentResolution.Context, - }, nil -} - -// Update did doc. -func (v *VDR) Update(_ *docdid.Doc, _ ...vdrapi.DIDMethodOption) error { - return fmt.Errorf("not implemented") -} - -// Deactivate did doc. -func (v *VDR) Deactivate(_ string, _ ...vdrapi.DIDMethodOption) error { - return fmt.Errorf("not implemented") -} - -func (v *VDR) sendRequest(req []byte, _ sidetree.GetEndpointsFunc) ([]byte, error) { - didResolution, err := v.sidetreeDocHandler.ProcessOperation(req) - if err != nil { - return nil, err - } - - return json.Marshal(didResolution) -} - -type pk struct { - value []byte - publicKey *doc.PublicKey -} - -func getSidetreePublicKeys(didDoc *docdid.Doc) (map[string]*pk, error) { //nolint:funlen,gocyclo - pksMap := make(map[string]*pk) - - ver := make([]docdid.Verification, 0) - - ver = append(ver, didDoc.Authentication...) - ver = append(ver, didDoc.AssertionMethod...) - ver = append(ver, didDoc.CapabilityDelegation...) - ver = append(ver, didDoc.CapabilityInvocation...) - ver = append(ver, didDoc.KeyAgreement...) - - for _, v := range ver { - var purpose string - - switch v.Relationship { //nolint: exhaustive - case docdid.Authentication: - purpose = doc.KeyPurposeAuthentication - case docdid.AssertionMethod: - purpose = doc.KeyPurposeAssertionMethod - case docdid.CapabilityDelegation: - purpose = doc.KeyPurposeCapabilityDelegation - case docdid.CapabilityInvocation: - purpose = doc.KeyPurposeCapabilityInvocation - case docdid.KeyAgreement: - purpose = doc.KeyPurposeKeyAgreement - default: - return nil, fmt.Errorf("vm relationship %d not supported", v.Relationship) - } - - s := strings.Split(v.VerificationMethod.ID, "#") - - id := s[0] - if len(s) > 1 { - id = s[1] - } - - value, ok := pksMap[id] - if ok { - value.publicKey.Purposes = append(value.publicKey.Purposes, purpose) - - continue - } - - switch { - case v.VerificationMethod.JSONWebKey() != nil: - pksMap[id] = &pk{ - publicKey: &doc.PublicKey{ - ID: id, - Type: v.VerificationMethod.Type, - Purposes: []string{purpose}, - JWK: *v.VerificationMethod.JSONWebKey(), - }, - value: v.VerificationMethod.Value, - } - case v.VerificationMethod.Value != nil: - pksMap[id] = &pk{ - publicKey: &doc.PublicKey{ - ID: id, - Type: v.VerificationMethod.Type, - Purposes: []string{purpose}, - B58Key: base58.Encode(v.VerificationMethod.Value), - }, - value: v.VerificationMethod.Value, - } - default: - return nil, fmt.Errorf("verificationMethod needs either JSONWebKey or Base58 key") - } - } - - return pksMap, nil -} - -// Option configures the long-form vdr. -type Option func(opts *VDR) - -// WithDocumentLoader overrides the default JSONLD document loader used when processing JSONLD DID Documents. -func WithDocumentLoader(l jsonld.DocumentLoader) Option { - return func(opts *VDR) { - opts.documentLoader = l - } -} - -// WithDIDMethod overrides the default did method. -func WithDIDMethod(method string) Option { - return func(opts *VDR) { - opts.method = method - } -} diff --git a/method/sidetreelongform/vdr_test.go b/method/sidetreelongform/vdr_test.go deleted file mode 100644 index 9db6b18..0000000 --- a/method/sidetreelongform/vdr_test.go +++ /dev/null @@ -1,508 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -package sidetreelongform - -import ( - "crypto/ecdsa" - "crypto/ed25519" - "crypto/elliptic" - "crypto/rand" - "encoding/json" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "github.com/trustbloc/bbs-signature-go/bbs12381g2pub" - ld "github.com/trustbloc/did-go/doc/ld/documentloader" - mockldstore "github.com/trustbloc/did-go/doc/ld/mock" - ldstore "github.com/trustbloc/did-go/doc/ld/store" - "github.com/trustbloc/did-go/method/sidetreelongform/sidetree/option/create" - "github.com/trustbloc/kms-go/doc/jose/jwk" - "github.com/trustbloc/kms-go/doc/jose/jwk/jwksupport" - "github.com/trustbloc/sidetree-go/pkg/document" - - ariesdid "github.com/trustbloc/did-go/doc/did" - model "github.com/trustbloc/did-go/doc/did/endpoint" - vdrapi "github.com/trustbloc/did-go/vdr/api" -) - -const ( - p256KeyType = "P256" - p384KeyType = "P384" - bls12381G2KeyType = "Bls12381G2" - ed25519KeyType = "Ed25519" - - notImplemented = "not implemented" -) - -func TestVDRI_Accept(t *testing.T) { - t.Run("test success - did accept option", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - require.True(t, v.Accept(defaultDIDMethod, - vdrapi.WithOption(DIDAcceptOpt, "did:ion:suffix:request"))) - }) - - t.Run("test success - vdr accept option", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - require.True(t, v.Accept(defaultDIDMethod, - vdrapi.WithOption(VDRAcceptOpt, "long-form"))) - }) - - t.Run("test success - vdr accept option and did accept option (vdr accept is processed first)", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - require.True(t, v.Accept(defaultDIDMethod, - vdrapi.WithOption(VDRAcceptOpt, "long-form"), - vdrapi.WithOption(DIDAcceptOpt, "did:ion:suffix"))) - }) - - t.Run("test return false - different method", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - require.False(t, v.Accept("different")) - }) - - t.Run("test return false - short form did", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - require.False(t, v.Accept(defaultDIDMethod, - vdrapi.WithOption(DIDAcceptOpt, "did:ion:suffix"))) - }) - - t.Run("test return false - vdr accept option different", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - require.False(t, v.Accept(defaultDIDMethod, - vdrapi.WithOption(VDRAcceptOpt, "different"))) - }) - - t.Run("test return false - no options provided", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - require.False(t, v.Accept(defaultDIDMethod)) - }) -} - -func TestVDRI_Close(t *testing.T) { - t.Run("test success", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - - err = v.Close() - require.NoError(t, err) - }) -} - -func TestVDRI_Options(t *testing.T) { - t.Run("test success", func(t *testing.T) { - docLoader := createTestDocLoader(t) - - v, err := New(WithDIDMethod("different"), WithDocumentLoader(docLoader)) - require.NoError(t, err) - - err = v.Close() - require.NoError(t, err) - }) -} - -func TestVDRI_Read(t *testing.T) { - t.Run("test success", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - - longFormDID := fmt.Sprintf("did:%s:%s:%s", defaultDIDMethod, didSuffix, requestJCS) - - docResolution, err := v.Read(longFormDID) - require.NoError(t, err) - require.NotNil(t, docResolution) - - err = prettyPrint(docResolution) - require.NoError(t, err) - - didDoc := docResolution.DIDDocument - - require.Equal(t, longFormDID, didDoc.ID) - require.Equal(t, 1, len(didDoc.VerificationMethod)) - require.Equal(t, fmt.Sprintf("%s#signingKey", longFormDID), didDoc.VerificationMethod[0].ID) - require.Equal(t, longFormDID, didDoc.VerificationMethod[0].Controller) - - require.Equal(t, 1, len(didDoc.AssertionMethod)) - require.Equal(t, 1, len(didDoc.Authentication)) - require.Equal(t, 1, len(didDoc.CapabilityInvocation)) - require.Equal(t, 1, len(didDoc.CapabilityDelegation)) - require.Equal(t, 1, len(didDoc.KeyAgreement)) - - require.Equal(t, - docResolution.DocumentMetadata.EquivalentID[0], - fmt.Sprintf("did:%s:%s", defaultDIDMethod, didSuffix)) - }) - - t.Run("test sidetree document handler error", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - - v.sidetreeDocHandler = &mockDocHandler{Err: fmt.Errorf("document handler error")} - - longFormDID := fmt.Sprintf("did:%s:%s:%s", defaultDIDMethod, didSuffix, requestJCS) - - docResolution, err := v.Read(longFormDID) - require.Error(t, err) - require.Nil(t, docResolution) - require.Contains(t, err.Error(), "document handler error") - }) - - t.Run("test parsing sidetree resolution result", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - - v.sidetreeDocHandler = &mockDocHandler{ResolutionResult: &document.ResolutionResult{}} - - longFormDID := fmt.Sprintf("did:%s:%s:%s", defaultDIDMethod, didSuffix, requestJCS) - - docResolution, err := v.Read(longFormDID) - require.Error(t, err) - require.Nil(t, docResolution) - require.Contains(t, err.Error(), "document payload is not provided") - }) -} - -func TestVDRI_Create(t *testing.T) { - pubKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - didDoc := &ariesdid.Doc{} - - vm, err := createVerificationMethod(ed25519KeyType, pubKey, "abc", "Ed25519VerificationKey2020") - require.NoError(t, err) - - didDoc.Authentication = append(didDoc.Authentication, - *ariesdid.NewReferencedVerification(vm, ariesdid.Authentication)) - - didDoc.AssertionMethod = append(didDoc.AssertionMethod, - *ariesdid.NewReferencedVerification(vm, ariesdid.AssertionMethod)) - - didDoc.CapabilityDelegation = append(didDoc.CapabilityDelegation, - *ariesdid.NewReferencedVerification(vm, ariesdid.CapabilityDelegation)) - - didDoc.CapabilityInvocation = append(didDoc.CapabilityInvocation, - *ariesdid.NewReferencedVerification(vm, ariesdid.CapabilityInvocation)) - - // Note: ion doesn't support publicKeyBase58 property - vm2 := ariesdid.NewVerificationMethodFromBytes("xyz", "Ed25519VerificationKey2018", "", pubKey) - didDoc.AssertionMethod = append(didDoc.AssertionMethod, - *ariesdid.NewReferencedVerification(vm2, ariesdid.AssertionMethod)) - - didDoc.Service = append(didDoc.Service, - ariesdid.Service{ - ID: "svc", - Type: "type", - // Note: ion doesn't support an array of service endpoints - do NOT use V2 - ServiceEndpoint: model.NewDIDCommV1Endpoint("https://example.com"), - }) - - // Note: ion doesn't support also-known-as patch - // didDoc.AlsoKnownAs = []string{"https://myblog.example/"} - - t.Run("test success", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - - recoveryKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - updateKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - docResolution, err := v.Create(didDoc, vdrapi.WithOption(UpdatePublicKeyOpt, updateKey), - vdrapi.WithOption(RecoveryPublicKeyOpt, recoveryKey)) - require.NoError(t, err) - require.NotEmpty(t, docResolution.DIDDocument.ID) - - err = prettyPrint(docResolution) - require.NoError(t, err) - - didResolution, err := v.Read(docResolution.DIDDocument.ID) - require.NoError(t, err) - require.NotNil(t, didResolution) - - err = prettyPrint(didResolution) - require.NoError(t, err) - }) - - t.Run("test success - simple did (one public key only)", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - - testVM, err := createVerificationMethod(ed25519KeyType, pubKey, "abc", "Ed25519VerificationKey2020") - require.NoError(t, err) - - simpleDoc := &ariesdid.Doc{} - - simpleDoc.Authentication = append(simpleDoc.Authentication, - *ariesdid.NewReferencedVerification(testVM, ariesdid.Authentication)) - - simpleDoc.AssertionMethod = append(simpleDoc.AssertionMethod, - *ariesdid.NewReferencedVerification(testVM, ariesdid.AssertionMethod)) - - docResolution, err := v.Create(simpleDoc) - require.NoError(t, err) - require.NotEmpty(t, docResolution.DIDDocument.ID) - - err = prettyPrint(docResolution) - require.NoError(t, err) - - didResolution, err := v.Read(docResolution.DIDDocument.ID) - require.NoError(t, err) - require.NotNil(t, didResolution) - - err = prettyPrint(didResolution) - require.NoError(t, err) - }) - - t.Run("test success - simple did (one service only)", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - - recoveryKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - updateKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - simpleDoc := &ariesdid.Doc{} - - simpleDoc.Service = append(simpleDoc.Service, - ariesdid.Service{ - ID: "svc", - Type: "type", - ServiceEndpoint: model.NewDIDCommV1Endpoint("https://example.com"), - }) - - docResolution, err := v.Create(simpleDoc, vdrapi.WithOption(UpdatePublicKeyOpt, updateKey), - vdrapi.WithOption(RecoveryPublicKeyOpt, recoveryKey)) - require.NoError(t, err) - require.NotEmpty(t, docResolution.DIDDocument.ID) - - err = prettyPrint(docResolution) - require.NoError(t, err) - - didResolution, err := v.Read(docResolution.DIDDocument.ID) - require.NoError(t, err) - require.NotNil(t, didResolution) - - err = prettyPrint(didResolution) - require.NoError(t, err) - }) - - t.Run("test sidetree client error", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - - v.sidetreeClient = &mockSidetreeClient{Err: fmt.Errorf("sidetree client error")} - - recoveryKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - updateKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - docResolution, err := v.Create(didDoc, vdrapi.WithOption(UpdatePublicKeyOpt, updateKey), - vdrapi.WithOption(RecoveryPublicKeyOpt, recoveryKey)) - require.Error(t, err) - require.Nil(t, docResolution) - require.Contains(t, err.Error(), "sidetree client error") - }) - - t.Run("test sidetree document handler error", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - - v.sidetreeDocHandler = &mockDocHandler{Err: fmt.Errorf("sidetree document handler error")} - - recoveryKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - updateKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - docResolution, err := v.Create(didDoc, vdrapi.WithOption(UpdatePublicKeyOpt, updateKey), - vdrapi.WithOption(RecoveryPublicKeyOpt, recoveryKey)) - require.Error(t, err) - require.Nil(t, docResolution) - require.Contains(t, err.Error(), "sidetree document handler error") - }) - - t.Run("test invalid key", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - - updateKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - recoveryKey, _, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - - invalidVM := ariesdid.NewVerificationMethodFromBytes("xyz", "Ed25519VerificationKey2018", "", pubKey) - invalidVM.Value = nil - - invalidDoc := &ariesdid.Doc{} - - invalidDoc.Authentication = append(didDoc.Authentication, - *ariesdid.NewReferencedVerification(invalidVM, ariesdid.Authentication)) - - docResolution, err := v.Create(invalidDoc, vdrapi.WithOption(UpdatePublicKeyOpt, updateKey), - vdrapi.WithOption(RecoveryPublicKeyOpt, recoveryKey)) - require.Error(t, err) - require.Nil(t, docResolution) - - require.Contains(t, err.Error(), "verificationMethod needs either JSONWebKey or Base58 key") - }) -} - -func TestVDRI_Update(t *testing.T) { - t.Run("error - function not implemented", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - require.NotNil(t, v) - - err = v.Update(&ariesdid.Doc{}) - require.Error(t, err) - require.Contains(t, err.Error(), notImplemented) - }) -} - -func TestVDRI_Deactivated(t *testing.T) { - t.Run("error - function not implemented", func(t *testing.T) { - v, err := New() - require.NoError(t, err) - require.NotNil(t, v) - - err = v.Deactivate("id") - require.Error(t, err) - require.Contains(t, err.Error(), notImplemented) - }) -} - -type mockSidetreeClient struct { - DocResolution *ariesdid.DocResolution - Err error -} - -func (m *mockSidetreeClient) CreateDID(_ ...create.Option) (*ariesdid.DocResolution, error) { - if m.Err != nil { - return nil, m.Err - } - - return m.DocResolution, nil -} - -type mockDocHandler struct { - Err error - ResolutionResult *document.ResolutionResult -} - -func (dh *mockDocHandler) ResolveDocument(_ string, _ ...document.ResolutionOption) ( - *document.ResolutionResult, error) { - if dh.Err != nil { - return nil, dh.Err - } - - return dh.ResolutionResult, nil -} - -func (dh *mockDocHandler) ProcessOperation(_ []byte) (*document.ResolutionResult, error) { - if dh.Err != nil { - return nil, dh.Err - } - - return dh.ResolutionResult, nil -} - -type mockLDStoreProvider struct { - ContextStore ldstore.ContextStore - RemoteProviderStore ldstore.RemoteProviderStore -} - -func (m *mockLDStoreProvider) JSONLDContextStore() ldstore.ContextStore { - return m.ContextStore -} - -func (m *mockLDStoreProvider) JSONLDRemoteProviderStore() ldstore.RemoteProviderStore { - return m.RemoteProviderStore -} - -func createTestDocLoader(t *testing.T) *ld.DocumentLoader { - t.Helper() - - p := &mockLDStoreProvider{ - ContextStore: mockldstore.NewMockContextStore(), - RemoteProviderStore: mockldstore.NewMockRemoteProviderStore(), - } - - loader, err := ld.NewDocumentLoader(p) - require.NoError(t, err) - - return loader -} - -func prettyPrint(result interface{}) error { - b, err := json.MarshalIndent(result, "", " ") - if err != nil { - return err - } - - fmt.Println(string(b)) - - return nil -} - -func createVerificationMethod(keyType string, pubKey []byte, kid, - signatureSuite string) (*ariesdid.VerificationMethod, error) { - var j *jwk.JWK - - var err error - - switch keyType { - case p256KeyType: - x, y := elliptic.Unmarshal(elliptic.P256(), pubKey) - - j, err = jwksupport.JWKFromKey(&ecdsa.PublicKey{X: x, Y: y, Curve: elliptic.P256()}) - if err != nil { - return nil, err - } - case p384KeyType: - x, y := elliptic.Unmarshal(elliptic.P384(), pubKey) - - j, err = jwksupport.JWKFromKey(&ecdsa.PublicKey{X: x, Y: y, Curve: elliptic.P384()}) - if err != nil { - return nil, err - } - case bls12381G2KeyType: - pk, e := bbs12381g2pub.UnmarshalPublicKey(pubKey) - if e != nil { - return nil, e - } - - j, err = jwksupport.JWKFromKey(pk) - if err != nil { - return nil, err - } - default: - j, err = jwksupport.JWKFromKey(ed25519.PublicKey(pubKey)) - if err != nil { - return nil, err - } - } - - return ariesdid.NewVerificationMethodFromJWK(kid, signatureSuite, "", j) -} - -const didSuffix = `EiD9H4OHw5X4ctS1Q1G9LxmyEed9WDBW_QZ4VMpuOtRciw` - -//nolint:lll -const requestJCS = `eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJzaWduaW5nS2V5IiwicHVibGljS2V5SndrIjp7ImNydiI6InNlY3AyNTZrMSIsImt0eSI6IkVDIiwieCI6IndkRGZEakwxRlFET3NwcC1xdmRLUUtyNzllbTdOczJFNVNBVWE5aElRaTQiLCJ5IjoiUGZmc0hEYXA1X0t3UlZwNzgtaUJaQm5XQTZMS3p6bGIxSXJ3VWhFakpuOCJ9LCJwdXJwb3NlcyI6WyJhdXRoZW50aWNhdGlvbiIsImFzc2VydGlvbk1ldGhvZCIsImNhcGFiaWxpdHlJbnZvY2F0aW9uIiwiY2FwYWJpbGl0eURlbGVnYXRpb24iLCJrZXlBZ3JlZW1lbnQiXSwidHlwZSI6IkVjZHNhU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOSJ9XSwic2VydmljZXMiOltdfX1dLCJ1cGRhdGVDb21taXRtZW50IjoiRWlBazRmbkFKSTJuZ1Z5ZjhrZ05fbUI5emhmX2FKcmdwa2tlalVIbTR1X3gzQSJ9LCJzdWZmaXhEYXRhIjp7ImRlbHRhSGFzaCI6IkVpQVRaWi1jclh5OXFYeGhGdkFFZElhU0pLY0tTWTVubkZ5bkJCSWtsODF5N1EiLCJyZWNvdmVyeUNvbW1pdG1lbnQiOiJFaUFOOHQ3UHlZYmtONFc3ZEVZX1JZX25YWUNlc1JPQl9mUWxzdWx3eVNyYVF3In0sInR5cGUiOiJjcmVhdGUifQ` diff --git a/pkg/canonicalizer/canonicalizer.go b/pkg/canonicalizer/canonicalizer.go new file mode 100644 index 0000000..ea6faa2 --- /dev/null +++ b/pkg/canonicalizer/canonicalizer.go @@ -0,0 +1,29 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package canonicalizer + +import ( + "encoding/json" + + "github.com/trustbloc/did-go/pkg/internal/jsoncanonicalizer" +) + +// MarshalCanonical is using JCS RFC canonicalization. +func MarshalCanonical(value interface{}) ([]byte, error) { + valueBytes, ok := value.([]byte) + + if !ok { + var err error + + valueBytes, err = json.Marshal(value) + if err != nil { + return nil, err + } + } + + return jsoncanonicalizer.Transform(valueBytes) +} diff --git a/pkg/canonicalizer/canonicalizer_test.go b/pkg/canonicalizer/canonicalizer_test.go new file mode 100644 index 0000000..f9fcfd4 --- /dev/null +++ b/pkg/canonicalizer/canonicalizer_test.go @@ -0,0 +1,43 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package canonicalizer + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMarshalCanonical(t *testing.T) { + t.Run("success", func(t *testing.T) { + test := struct { + Beta string `json:"beta"` + Alpha string `json:"alpha"` + }{ + Beta: "beta", + Alpha: "alpha", + } + + result, err := MarshalCanonical(test) + require.NoError(t, err) + require.Equal(t, string(result), `{"alpha":"alpha","beta":"beta"}`) + }) + + t.Run("success - accepts bytes", func(t *testing.T) { + result, err := MarshalCanonical([]byte(`{"beta":"beta","alpha":"alpha"}`)) + require.NoError(t, err) + require.Equal(t, string(result), `{"alpha":"alpha","beta":"beta"}`) + }) + + t.Run("marshal error", func(t *testing.T) { + var c chan int + result, err := MarshalCanonical(c) + require.Error(t, err) + require.Empty(t, result) + require.Contains(t, err.Error(), "json: unsupported type: chan int") + }) +} diff --git a/pkg/internal/jsoncanonicalizer/README.md b/pkg/internal/jsoncanonicalizer/README.md new file mode 100644 index 0000000..5c91be7 --- /dev/null +++ b/pkg/internal/jsoncanonicalizer/README.md @@ -0,0 +1,4 @@ +## JSON Cononicalizer + +The files in this folder are copied AS-IS from [Cyberphone JSON Canonicalization Go Library](https://github.com/cyberphone/json-canonicalization/tree/master/go/src/webpki.org/jsoncanonicalizer). +The licence details are available at [LICENCE](https://github.com/cyberphone/json-canonicalization/blob/master/LICENSE). diff --git a/pkg/internal/jsoncanonicalizer/es6numfmt.go b/pkg/internal/jsoncanonicalizer/es6numfmt.go new file mode 100644 index 0000000..c48294b --- /dev/null +++ b/pkg/internal/jsoncanonicalizer/es6numfmt.go @@ -0,0 +1,94 @@ +// +// Copyright 2006-2019 WebPKI.org (http://webpki.org). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// This package converts numbers in IEEE-754 double precision into the +// format specified for JSON in EcmaScript Version 6 and forward. +// The core application for this is canonicalization: +// https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-02 + +// SPDX-License-Identifier: Apache-2.0 + +package jsoncanonicalizer + +import ( + "errors" + "math" + "strconv" + "strings" +) + +const invalidPattern uint64 = 0x7ff0000000000000 + +func NumberToJSON(ieeeF64 float64) (res string, err error) { + ieeeU64 := math.Float64bits(ieeeF64) + + // Special case: NaN and Infinity are invalid in JSON + if (ieeeU64 & invalidPattern) == invalidPattern { + return "null", errors.New("Invalid JSON number: " + strconv.FormatUint(ieeeU64, 16)) + } + + // Special case: eliminate "-0" as mandated by the ES6-JSON/JCS specifications + if ieeeF64 == 0 { // Right, this line takes both -0 and 0 + return "0", nil + } + + // Deal with the sign separately + var sign string = "" + if ieeeF64 < 0 { + ieeeF64 = -ieeeF64 + sign = "-" + } + + // ES6 has a unique "g" format + var format byte = 'e' + if ieeeF64 < 1e+21 && ieeeF64 >= 1e-6 { + format = 'f' + } + + // The following should (in "theory") do the trick: + es6Formatted := strconv.FormatFloat(ieeeF64, format, -1, 64) + + // Unfortunately Go version 1.11.4 is a bit buggy with respect to + // rounding for -1 precision which is dealt with below. + // https://github.com/golang/go/issues/29491 + exponent := strings.IndexByte(es6Formatted, 'e') + if exponent > 0 { + gform := strconv.FormatFloat(ieeeF64, 'g', 17, 64) + if len(gform) == len(es6Formatted) { + // "g" occasionally produces another result which also is the correct one + es6Formatted = gform + } + // Go outputs "1e+09" which must be rewritten as "1e+9" + if es6Formatted[exponent+2] == '0' { + es6Formatted = es6Formatted[:exponent+2] + es6Formatted[exponent+3:] + } + } else if strings.IndexByte(es6Formatted, '.') < 0 && len(es6Formatted) >= 12 { + i := len(es6Formatted) + for es6Formatted[i-1] == '0' { + i-- + } + if i != len(es6Formatted) { + fix := strconv.FormatFloat(ieeeF64, 'f', 0, 64) + if fix[i] >= '5' { + // "f" with precision 0 occasionally produces another result which also is + // the correct one although it must be rounded to match the -1 precision + // (which fortunately seems to be correct with respect to trailing zeroes) + es6Formatted = fix[:i-1] + string(fix[i-1]+1) + es6Formatted[i:] + } + } + } + return sign + es6Formatted, nil +} diff --git a/pkg/internal/jsoncanonicalizer/jsoncanonicalizer.go b/pkg/internal/jsoncanonicalizer/jsoncanonicalizer.go new file mode 100644 index 0000000..476b21b --- /dev/null +++ b/pkg/internal/jsoncanonicalizer/jsoncanonicalizer.go @@ -0,0 +1,379 @@ +// +// Copyright 2006-2019 WebPKI.org (http://webpki.org). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// This package transforms JSON data in UTF-8 according to: +// https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-02 + +// SPDX-License-Identifier: Apache-2.0 + +package jsoncanonicalizer + +import ( + "container/list" + "errors" + "fmt" + "strconv" + "strings" + "unicode/utf16" +) + +type nameValueType struct { + name string + sortKey []uint16 + value string +} + +// JSON standard escapes (modulo \u) +var asciiEscapes = []byte{'\\', '"', 'b', 'f', 'n', 'r', 't'} +var binaryEscapes = []byte{'\\', '"', '\b', '\f', '\n', '\r', '\t'} + +// JSON literals +var literals = []string{"true", "false", "null"} + +func Transform(jsonData []byte) (result []byte, e error) { + // JSON data MUST be UTF-8 encoded + var jsonDataLength int = len(jsonData) + + // Current pointer in jsonData + var index int = 0 + + // "Forward" declarations are needed for closures referring each other + var parseElement func() string + var parseSimpleType func() string + var parseQuotedString func() string + var parseObject func() string + var parseArray func() string + + var globalError error = nil + + checkError := func(e error) { + // We only honor the first reported error + if globalError == nil { + globalError = e + } + } + + setError := func(msg string) { + checkError(errors.New(msg)) + } + + isWhiteSpace := func(c byte) bool { + return c == 0x20 || c == 0x0a || c == 0x0d || c == 0x09 + } + + nextChar := func() byte { + if index < jsonDataLength { + c := jsonData[index] + if c > 0x7f { + setError("Unexpected non-ASCII character") + } + index++ + return c + } + setError("Unexpected EOF reached") + return '"' + } + + scan := func() byte { + for { + c := nextChar() + if isWhiteSpace(c) { + continue + } + return c + } + } + + scanFor := func(expected byte) { + c := scan() + if c != expected { + setError("Expected '" + string(expected) + "' but got '" + string(c) + "'") + } + } + + getUEscape := func() rune { + start := index + nextChar() + nextChar() + nextChar() + nextChar() + if globalError != nil { + return 0 + } + u16, err := strconv.ParseUint(string(jsonData[start:index]), 16, 64) + checkError(err) + return rune(u16) + } + + testNextNonWhiteSpaceChar := func() byte { + save := index + c := scan() + index = save + return c + } + + decorateString := func(rawUTF8 string) string { + var quotedString strings.Builder + quotedString.WriteByte('"') + CoreLoop: + for _, c := range []byte(rawUTF8) { + // Is this within the JSON standard escapes? + for i, esc := range binaryEscapes { + if esc == c { + quotedString.WriteByte('\\') + quotedString.WriteByte(asciiEscapes[i]) + continue CoreLoop + } + } + if c < 0x20 { + // Other ASCII control characters must be escaped with \uhhhh + quotedString.WriteString(fmt.Sprintf("\\u%04x", c)) + } else { + quotedString.WriteByte(c) + } + } + quotedString.WriteByte('"') + return quotedString.String() + } + + parseQuotedString = func() string { + var rawString strings.Builder + CoreLoop: + for globalError == nil { + var c byte + if index < jsonDataLength { + c = jsonData[index] + index++ + } else { + nextChar() + break + } + if c == '"' { + break + } + if c < ' ' { + setError("Unterminated string literal") + } else if c == '\\' { + // Escape sequence + c = nextChar() + if c == 'u' { + // The \u escape + firstUTF16 := getUEscape() + if utf16.IsSurrogate(firstUTF16) { + // If the first UTF-16 code unit has a certain value there must be + // another succeeding UTF-16 code unit as well + if nextChar() != '\\' || nextChar() != 'u' { + setError("Missing surrogate") + } else { + // Output the UTF-32 code point as UTF-8 + rawString.WriteRune(utf16.DecodeRune(firstUTF16, getUEscape())) + } + } else { + // Single UTF-16 code identical to UTF-32. Output as UTF-8 + rawString.WriteRune(firstUTF16) + } + } else if c == '/' { + // Benign but useless escape + rawString.WriteByte('/') + } else { + // The JSON standard escapes + for i, esc := range asciiEscapes { + if esc == c { + rawString.WriteByte(binaryEscapes[i]) + continue CoreLoop + } + } + setError("Unexpected escape: \\" + string(c)) + } + } else { + // Just an ordinary ASCII character alternatively a UTF-8 byte + // outside of ASCII. + // Note that properly formatted UTF-8 never clashes with ASCII + // making byte per byte search for ASCII break characters work + // as expected. + rawString.WriteByte(c) + } + } + return rawString.String() + } + + parseSimpleType = func() string { + var token strings.Builder + index-- + for globalError == nil { + c := testNextNonWhiteSpaceChar() + if c == ',' || c == ']' || c == '}' { + break + } + c = nextChar() + if isWhiteSpace(c) { + break + } + token.WriteByte(c) + } + if token.Len() == 0 { + setError("Missing argument") + } + value := token.String() + // Is it a JSON literal? + for _, literal := range literals { + if literal == value { + return literal + } + } + // Apparently not so we assume that it is a I-JSON number + ieeeF64, err := strconv.ParseFloat(value, 64) + checkError(err) + value, err = NumberToJSON(ieeeF64) + checkError(err) + return value + } + + parseElement = func() string { + switch scan() { + case '{': + return parseObject() + case '"': + return decorateString(parseQuotedString()) + case '[': + return parseArray() + default: + return parseSimpleType() + } + } + + parseArray = func() string { + var arrayData strings.Builder + arrayData.WriteByte('[') + var next bool = false + for globalError == nil && testNextNonWhiteSpaceChar() != ']' { + if next { + scanFor(',') + arrayData.WriteByte(',') + } else { + next = true + } + arrayData.WriteString(parseElement()) + } + scan() + arrayData.WriteByte(']') + return arrayData.String() + } + + lexicographicallyPrecedes := func(sortKey []uint16, e *list.Element) bool { + // Find the minimum length of the sortKeys + oldSortKey := e.Value.(nameValueType).sortKey + minLength := len(oldSortKey) + if minLength > len(sortKey) { + minLength = len(sortKey) + } + for q := 0; q < minLength; q++ { + diff := int(sortKey[q]) - int(oldSortKey[q]) + if diff < 0 { + // Smaller => Precedes + return true + } else if diff > 0 { + // Bigger => No match + return false + } + // Still equal => Continue + } + // The sortKeys compared equal up to minLength + if len(sortKey) < len(oldSortKey) { + // Shorter => Precedes + return true + } + if len(sortKey) == len(oldSortKey) { + setError("Duplicate key: " + e.Value.(nameValueType).name) + } + // Longer => No match + return false + } + + parseObject = func() string { + nameValueList := list.New() + var next bool = false + CoreLoop: + for globalError == nil && testNextNonWhiteSpaceChar() != '}' { + if next { + scanFor(',') + } + next = true + scanFor('"') + rawUTF8 := parseQuotedString() + if globalError != nil { + break + } + // Sort keys on UTF-16 code units + // Since UTF-8 doesn't have endianess this is just a value transformation + // In the Go case the transformation is UTF-8 => UTF-32 => UTF-16 + sortKey := utf16.Encode([]rune(rawUTF8)) + scanFor(':') + nameValue := nameValueType{rawUTF8, sortKey, parseElement()} + for e := nameValueList.Front(); e != nil; e = e.Next() { + // Check if the key is smaller than a previous key + if lexicographicallyPrecedes(sortKey, e) { + // Precedes => Insert before and exit sorting + nameValueList.InsertBefore(nameValue, e) + continue CoreLoop + } + // Continue searching for a possibly succeeding sortKey + // (which is straightforward since the list is ordered) + } + // The sortKey is either the first or is succeeding all previous sortKeys + nameValueList.PushBack(nameValue) + } + // Scan away '}' + scan() + // Now everything is sorted so we can properly serialize the object + var objectData strings.Builder + objectData.WriteByte('{') + next = false + for e := nameValueList.Front(); e != nil; e = e.Next() { + if next { + objectData.WriteByte(',') + } + next = true + nameValue := e.Value.(nameValueType) + objectData.WriteString(decorateString(nameValue.name)) + objectData.WriteByte(':') + objectData.WriteString(nameValue.value) + } + objectData.WriteByte('}') + return objectData.String() + } + + // /////////////////////////////////////////////// + // This is where Transform actually begins... // + // /////////////////////////////////////////////// + var transformed string + + if testNextNonWhiteSpaceChar() == '[' { + scan() + transformed = parseArray() + } else { + scanFor('{') + transformed = parseObject() + } + for index < jsonDataLength { + if !isWhiteSpace(jsonData[index]) { + setError("Improperly terminated JSON object") + break + } + index++ + } + return []byte(transformed), globalError +}