From a105f6c30d1499f1583f3816346720b5717a7773 Mon Sep 17 00:00:00 2001 From: "sudesh.shetty" Date: Thu, 22 Apr 2021 17:26:30 -0400 Subject: [PATCH] feat: vc wallet EDV storage - if wallet user provides EDV settings during profile creationfor storage then wallet will create internal EDV client instance for storing wallet contents - for now, encryption and MAC key IDs has to be provided by client user during profile creation/update - closes #2757 Signed-off-by: sudesh.shetty --- go.mod | 24 ++-- go.sum | 54 +++++++++ pkg/wallet/contents.go | 23 ++-- pkg/wallet/contents_test.go | 205 +++++++++++++++-------------------- pkg/wallet/kmsclient_test.go | 37 ++++++- pkg/wallet/options.go | 17 ++- pkg/wallet/profile.go | 84 ++++++++++++-- pkg/wallet/profile_test.go | 89 +++++++++++++++ pkg/wallet/storage.go | 139 ++++++++++++++++++++++++ pkg/wallet/storage_test.go | 120 ++++++++++++++++++++ pkg/wallet/wallet.go | 18 ++- pkg/wallet/wallet_test.go | 96 +++++++++++++--- 12 files changed, 726 insertions(+), 180 deletions(-) create mode 100644 pkg/wallet/storage.go create mode 100644 pkg/wallet/storage_test.go diff --git a/go.mod b/go.mod index e659dbcb24..922b7e4d8f 100644 --- a/go.mod +++ b/go.mod @@ -9,20 +9,20 @@ require ( github.com/PaesslerAG/jsonpath v0.1.1 github.com/VictoriaMetrics/fastcache v1.5.7 github.com/bluele/gcache v0.0.0-20190518031135-bc40bd653833 - github.com/btcsuite/btcd v0.20.1-beta - github.com/btcsuite/btcutil v1.0.1 + github.com/btcsuite/btcd v0.21.0-beta + github.com/btcsuite/btcutil v1.0.2 github.com/cenkalti/backoff/v4 v4.0.2 github.com/golang/mock v1.4.4 - github.com/golang/protobuf v1.4.2 + github.com/golang/protobuf v1.5.2 github.com/google/tink/go v1.5.0 github.com/google/uuid v1.1.2 github.com/gorilla/mux v1.7.3 - github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210422133815-2ef2d99cb692 - github.com/hyperledger/aries-framework-go/spi v0.0.0-20210422133815-2ef2d99cb692 + github.com/hyperledger/aries-framework-go/component/storage/edv v0.0.0-20210426170825-9321eebd7637 + github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210426170825-9321eebd7637 + github.com/hyperledger/aries-framework-go/spi v0.0.0-20210426170825-9321eebd7637 github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a github.com/kawamuray/jsonpath v0.0.0-20201211160320-7483bafabd7e - github.com/kilic/bls12-381 v0.0.0-20201104083100-a288617c07f1 - github.com/minio/sha256-simd v0.1.1 // indirect + github.com/kilic/bls12-381 v0.1.0 github.com/mitchellh/mapstructure v1.1.2 github.com/multiformats/go-multibase v0.0.1 github.com/multiformats/go-multihash v0.0.13 @@ -31,14 +31,14 @@ require ( github.com/rs/cors v1.7.0 github.com/square/go-jose/v3 v3.0.0-20200630053402-0a67ce9b0693 github.com/stretchr/testify v1.7.0 - github.com/teserakt-io/golang-ed25519 v0.0.0-20200315192543-8255be791ce4 + github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8 github.com/tidwall/gjson v1.6.7 github.com/tidwall/sjson v1.1.4 - github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonschema v1.2.0 - golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 - golang.org/x/sys v0.0.0-20201211090839-8ad439b19e0f // indirect - google.golang.org/protobuf v1.25.0 + golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b + golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 // indirect + google.golang.org/protobuf v1.26.0 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect nhooyr.io/websocket v1.8.3 ) diff --git a/go.sum b/go.sum index 0ed782b672..47831e548d 100644 --- a/go.sum +++ b/go.sum @@ -52,13 +52,19 @@ github.com/bluele/gcache v0.0.0-20190518031135-bc40bd653833 h1:yCfXxYaelOyqnia8F github.com/bluele/gcache v0.0.0-20190518031135-bc40bd653833/go.mod h1:8c4/i2VlovMO2gBnHGQPN5EJw+H0lx1u/5p+cgsXtCk= github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M= +github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v1.0.1 h1:GKOz8BnRjYrb/JTKgaOk+zh26NWNdSNvdvv0xoAZMSA= github.com/btcsuite/btcutil v1.0.1/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= +github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/cenkalti/backoff/v4 v4.0.2 h1:JIufpQLbh4DkbQoii76ItQIUFzevQSqOLZca4eamEDs= @@ -75,6 +81,7 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -119,6 +126,9 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -132,6 +142,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -174,16 +185,39 @@ github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvh github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hyperledger/aries-framework-go v0.1.7-0.20210409151411-eeeb8508bd87/go.mod h1:9Mz/wkgyzXKxuOZObASCWRprt30P+xSsL08T8VBFRnk= +github.com/hyperledger/aries-framework-go v0.1.7-0.20210421203733-b5dfd703a8fc/go.mod h1:tBgxVOKcNero3QI21iNf3oxxHkgRMDOby937cqHEvW4= +github.com/hyperledger/aries-framework-go/component/storage/edv v0.0.0-20210422133815-2ef2d99cb692 h1:anEytYaCtUeVS4UpivolNp8S4ZPLIQaBrJAiZMh5CwE= +github.com/hyperledger/aries-framework-go/component/storage/edv v0.0.0-20210422133815-2ef2d99cb692/go.mod h1:Vw8AblyCa1h6mVbNvbMXeZdlXVGu6Cq+TXZhD4oqvwE= +github.com/hyperledger/aries-framework-go/component/storage/edv v0.0.0-20210426170825-9321eebd7637 h1:irBeZRJoCqJ5LKTQOlQ3KUR6NbPfExfXOfHPlgY+NeY= +github.com/hyperledger/aries-framework-go/component/storage/edv v0.0.0-20210426170825-9321eebd7637/go.mod h1:7D+Y5J9cIsUrMGFAsIED+3bAPNjxp6ggXo0/kT5N6BI= +github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210320144851-40976de98ccf/go.mod h1:HVV8sifdHIyLkrlgmK/6+3YWKnOJPUfoNU+4SwQqMSs= +github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210409151411-eeeb8508bd87/go.mod h1:kJT7bcaKsvk1lMp2jqS8srF+ZUie2H4MoPbL2V29dgA= +github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210421203733-b5dfd703a8fc/go.mod h1:uGc7F3tXQIY6xjs8VEI6/oxp4ZDXDfGjPMCTgax5Zhc= github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210422133815-2ef2d99cb692 h1:y9eMnxX9OyHsKTAn2d0HO2vcfvXwldtaexdYoRAagrk= github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210422133815-2ef2d99cb692/go.mod h1:uGc7F3tXQIY6xjs8VEI6/oxp4ZDXDfGjPMCTgax5Zhc= +github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210426170825-9321eebd7637 h1:juMe7AU8vqipF4/qpBKDnG1F1gDcZjrtrjLUXAZQHvQ= +github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20210426170825-9321eebd7637/go.mod h1:aP6VnxeSbmD1OcV2f8y0dRV9fkIZp/+mzmgKxxmSJG4= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210310001230-bc1bd8ea889c/go.mod h1:fDr9wW00GJJl1lR1SFHmJW8utIocdvjO5RNhAYS05EY= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210310160016-d5eea2ecdd50/go.mod h1:fDr9wW00GJJl1lR1SFHmJW8utIocdvjO5RNhAYS05EY= github.com/hyperledger/aries-framework-go/spi v0.0.0-20210320144851-40976de98ccf/go.mod h1:fDr9wW00GJJl1lR1SFHmJW8utIocdvjO5RNhAYS05EY= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210322152545-e6ebe2c79a2a/go.mod h1:fDr9wW00GJJl1lR1SFHmJW8utIocdvjO5RNhAYS05EY= github.com/hyperledger/aries-framework-go/spi v0.0.0-20210409151411-eeeb8508bd87/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210412201938-efffe3eafcd1/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210421165342-de8f911415e3/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210421203733-b5dfd703a8fc/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= github.com/hyperledger/aries-framework-go/spi v0.0.0-20210422133815-2ef2d99cb692 h1:CW4qks+VsTKaeIXwb8tbc68bjVU9n6o6ek8vB5wyh54= github.com/hyperledger/aries-framework-go/spi v0.0.0-20210422133815-2ef2d99cb692/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210426170825-9321eebd7637 h1:5uM2oNHI2hktqVogdPbX/No6K46GXPfgADRDQBY9SOM= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20210426170825-9321eebd7637/go.mod h1:dBYKKD8U8U9o0g5BdNFFaRtjt9KTkiAYfQt+TTp+w1o= +github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210310160016-d5eea2ecdd50/go.mod h1:AybsT4/saiuxdVhK5CgOLIkcNMPZtX3GAUMOjHcLLjk= +github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210324232048-34ff560ed041/go.mod h1:eKGEEe+PJNDQo7kVif3sUKBWwnsQDkE3gD/QlpmukcQ= github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210409151411-eeeb8508bd87 h1:eGEPJ7L77Ov7/dT7IJVGmyIbHwFGwGChBS1GixD88c8= github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210409151411-eeeb8508bd87/go.mod h1:JHzDtgJLd0134iLFXLxGBjJF+Z+TgiElA/5oVgMazts= +github.com/hyperledger/aries-framework-go/test/component v0.0.0-20210421203733-b5dfd703a8fc/go.mod h1:asiCVCtH/nocWKhZRMz12aFgdUh8lRHqKis0M8Ei/4I= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o= github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -195,6 +229,8 @@ github.com/kawamuray/jsonpath v0.0.0-20201211160320-7483bafabd7e h1:Eh/0JuXDdcBH github.com/kawamuray/jsonpath v0.0.0-20201211160320-7483bafabd7e/go.mod h1:dz00yqWNWlKa9ff7RJzpnHPAPUazsid3yhVzXcsok94= github.com/kilic/bls12-381 v0.0.0-20201104083100-a288617c07f1 h1:fLyvBx6b/VrqcC1KlgTsPdpX3BcwGRWV8P6QfdgOLuw= github.com/kilic/bls12-381 v0.0.0-20201104083100-a288617c07f1/go.mod h1:gcwDl9YLyNc3H3wmPXamu+8evD8TYUa6BjTsWnvdn7A= +github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4= +github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.10.0 h1:92XGj1AcYzA6UrVdd4qIIBrT8OroryvRvdmg/IfmC7Y= @@ -234,6 +270,7 @@ github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXS github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 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.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -265,6 +302,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/teserakt-io/golang-ed25519 v0.0.0-20200315192543-8255be791ce4 h1:Sq/68UWgBzKT+pLTUTkSf0jS2IUwwXLFlZmeh+nAzQM= github.com/teserakt-io/golang-ed25519 v0.0.0-20200315192543-8255be791ce4/go.mod h1:9PdLyPiZIiW3UopXyRnPYyjUXSpiQNHRLu8fOsR3o8M= +github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8 h1:RBkacARv7qY5laaXGlF4wFB/tk5rnthhPb8oIBGoagY= +github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8/go.mod h1:9PdLyPiZIiW3UopXyRnPYyjUXSpiQNHRLu8fOsR3o8M= github.com/tidwall/gjson v1.6.7 h1:Mb1M9HZCRWEcXQ8ieJo7auYyyiSux6w9XN3AdTpxJrE= github.com/tidwall/gjson v1.6.7/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= @@ -298,9 +337,12 @@ golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -331,6 +373,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -358,6 +401,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -403,8 +447,13 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201211090839-8ad439b19e0f h1:QdHQnPce6K4XQewki9WNbG5KOROuDzqO3NaYjI1cXJ0= golang.org/x/sys v0.0.0-20201211090839-8ad439b19e0f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 h1:dXfMednGJh/SUUFjTLsWJz3P+TQt9qnR11GgeI3vWKs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -541,6 +590,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= @@ -554,6 +606,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/wallet/contents.go b/pkg/wallet/contents.go index 01865bc089..a714597959 100644 --- a/pkg/wallet/contents.go +++ b/pkg/wallet/contents.go @@ -100,29 +100,24 @@ var ( // contentStore is store for wallet contents for given user profile. type contentStore struct { - provider storage.Provider - storeID string + provider *storageProvider open storeOpenHandle close storeCloseHandle lock sync.RWMutex } // newContentStore returns new wallet content store instance. -func newContentStore(p storage.Provider, pr *profile) (*contentStore, error) { - err := p.SetStoreConfig(pr.ID, storage.StoreConfiguration{TagNames: []string{ - Collection.Name(), Credential.Name(), Connection.Name(), DIDResolutionResponse.Name(), Connection.Name(), Key.Name(), - }}) - if err != nil { - return nil, fmt.Errorf("failed to set store config for user '%s' : %w", pr.User, err) - } - - return &contentStore{open: storeLocked, close: noOp, provider: p, storeID: pr.ID}, nil +// will use underlying storage provider as content storage if profile doesn't have edv settings. +func newContentStore(p storage.Provider, pr *profile) *contentStore { + return &contentStore{open: storeLocked, close: noOp, provider: newWalletStorageProvider(pr, p)} } -func (cs *contentStore) Open() error { - store, err := cs.provider.OpenStore(cs.storeID) +func (cs *contentStore) Open(auth string) error { + store, err := cs.provider.OpenStore(auth, storage.StoreConfiguration{TagNames: []string{ + Collection.Name(), Credential.Name(), Connection.Name(), DIDResolutionResponse.Name(), Connection.Name(), Key.Name(), + }}) if err != nil { - return fmt.Errorf("failed to open store : %w", err) + return err } cs.lock.Lock() diff --git a/pkg/wallet/contents_test.go b/pkg/wallet/contents_test.go index fa5fa1a992..d2e75a4c07 100644 --- a/pkg/wallet/contents_test.go +++ b/pkg/wallet/contents_test.go @@ -188,45 +188,48 @@ func TestContentStores(t *testing.T) { t.Run("create new content store - success", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + // create new store + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) + require.Empty(t, sp.config.TagNames) + + // open store + require.NoError(t, contentStore.Open(token)) require.EqualValues(t, sp.config.TagNames, []string{"collection", "credential", "connection", "didResolutionResponse", "connection", "key"}) - }) - t.Run("create new content store - failure", func(t *testing.T) { - sp := getMockStorageProvider() - sp.failure = errors.New(sampleContenttErr) - - // set store config error - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.Empty(t, contentStore) - require.Error(t, err) - require.Contains(t, err.Error(), sampleContenttErr) - require.Contains(t, err.Error(), "failed to set store config for user") + // close store + contentStore.Close() + store, err := contentStore.open(token) + require.Empty(t, store) + require.True(t, errors.Is(err, ErrWalletLocked)) }) t.Run("open store - failure", func(t *testing.T) { sp := getMockStorageProvider() + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) + // open store error - sp.failure = nil sp.ErrOpenStoreHandle = errors.New(sampleContenttErr) - - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) - - err = contentStore.Open() + err := contentStore.Open(token) require.Error(t, err) require.Contains(t, err.Error(), sampleContenttErr) require.Contains(t, err.Error(), "failed to open store") + // set store config error + sp.ErrOpenStoreHandle = nil + sp.failure = errors.New(sampleContenttErr) + err = contentStore.Open(token) + require.Error(t, err) + require.Contains(t, err.Error(), sampleContenttErr) + require.Contains(t, err.Error(), "failed to set store config") + sp.ErrOpenStoreHandle = nil + sp.failure = nil sp.Store.ErrClose = errors.New(sampleContenttErr) - contentStore, err = newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) - require.NoError(t, contentStore.Open()) + contentStore = newContentStore(sp, &profile{ID: uuid.New().String()}) + require.NoError(t, contentStore.Open(token)) contentStore.Close() }) @@ -234,13 +237,12 @@ func TestContentStores(t *testing.T) { t.Run("save to store - success", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) - err = contentStore.Save(token, Collection, []byte(sampleContentValid)) + err := contentStore.Save(token, Collection, []byte(sampleContentValid)) require.NoError(t, err) // store is open but invalid auth token @@ -255,26 +257,24 @@ func TestContentStores(t *testing.T) { t.Run("save content to store without ID - success", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) - err = contentStore.Save(token, Collection, []byte(sampleContentNoID)) + err := contentStore.Save(token, Collection, []byte(sampleContentNoID)) require.NoError(t, err) }) t.Run("save to doc resolution to store - success", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) - err = contentStore.Save(token, DIDResolutionResponse, []byte(didResolutionResult)) + err := contentStore.Save(token, DIDResolutionResponse, []byte(didResolutionResult)) require.NoError(t, err) // get by DID ID @@ -295,12 +295,11 @@ func TestContentStores(t *testing.T) { sp := getMockStorageProvider() sampleUser := uuid.New().String() - contentStore, err := newContentStore(sp, &profile{ID: sampleUser}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: sampleUser}) require.NotEmpty(t, contentStore) // wallet locked - err = contentStore.Save(sampleFakeTkn, Key, []byte(sampleKeyContentBase58Valid)) + err := contentStore.Save(sampleFakeTkn, Key, []byte(sampleKeyContentBase58Valid)) require.True(t, errors.Is(err, ErrWalletLocked)) err = contentStore.Save(sampleFakeTkn, Key, []byte(sampleKeyContentJwkValid)) @@ -341,12 +340,11 @@ func TestContentStores(t *testing.T) { sp := getMockStorageProvider() sampleUser := uuid.New().String() - contentStore, err := newContentStore(sp, &profile{ID: sampleUser}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: sampleUser}) require.NotEmpty(t, contentStore) // wallet locked - err = contentStore.Save(token, Key, []byte("")) + err := contentStore.Save(token, Key, []byte("")) require.Error(t, err) require.Contains(t, err.Error(), "failed to read key contents") }) @@ -354,11 +352,10 @@ func TestContentStores(t *testing.T) { t.Run("save to doc resolution to store - failure", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - err = contentStore.Save(token, DIDResolutionResponse, []byte(sampleContentInvalid)) + err := contentStore.Save(token, DIDResolutionResponse, []byte(sampleContentInvalid)) require.Error(t, err) require.Contains(t, err.Error(), "invalid DID resolution response model") }) @@ -366,12 +363,11 @@ func TestContentStores(t *testing.T) { t.Run("save to store - failures", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) // invalid content type - err = contentStore.Save(token, ContentType("invalid"), []byte(sampleContentValid)) + err := contentStore.Save(token, ContentType("invalid"), []byte(sampleContentValid)) require.Error(t, err) require.Contains(t, err.Error(), "invalid content type 'invalid'") @@ -383,15 +379,14 @@ func TestContentStores(t *testing.T) { // store errors sp.Store.ErrPut = errors.New(sampleContenttErr) - contentStore, err = newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore = newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) // wallet locked err = contentStore.Save(token, Credential, []byte(sampleContentValid)) require.True(t, errors.Is(err, ErrWalletLocked)) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) err = contentStore.Save(token, Credential, []byte(sampleContentValid)) require.Error(t, err) @@ -406,11 +401,10 @@ func TestContentStores(t *testing.T) { t.Run("save to invalid content type - validation", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - err = contentStore.Save(token, "Test", []byte("{}")) + err := contentStore.Save(token, "Test", []byte("{}")) require.Error(t, err) require.Contains(t, err.Error(), "invalid content type") }) @@ -418,13 +412,12 @@ func TestContentStores(t *testing.T) { t.Run("save duplicate items - validation", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) - err = contentStore.Save(token, Collection, []byte(sampleContentValid)) + err := contentStore.Save(token, Collection, []byte(sampleContentValid)) require.NoError(t, err) // save again @@ -436,14 +429,13 @@ func TestContentStores(t *testing.T) { t.Run("get from store - success", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) // save - err = contentStore.Save(token, Collection, []byte(sampleContentValid)) + err := contentStore.Save(token, Collection, []byte(sampleContentValid)) require.NoError(t, err) // get @@ -456,15 +448,14 @@ func TestContentStores(t *testing.T) { sp := getMockStorageProvider() sp.Store.ErrGet = errors.New(sampleContenttErr) - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) content, err := contentStore.Get(token, "did:example:123456789abcdefghi", Collection) require.Empty(t, content) require.True(t, errors.Is(err, ErrWalletLocked)) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) // get content, err = contentStore.Get(token, "did:example:123456789abcdefghi", Collection) @@ -476,14 +467,13 @@ func TestContentStores(t *testing.T) { t.Run("remove from store - success", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) // save - err = contentStore.Save(token, Collection, []byte(sampleContentValid)) + err := contentStore.Save(token, Collection, []byte(sampleContentValid)) require.NoError(t, err) // get @@ -506,14 +496,13 @@ func TestContentStores(t *testing.T) { sp := getMockStorageProvider() sp.Store.ErrDelete = errors.New(sampleContenttErr) - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) // save - err = contentStore.Save(token, Collection, []byte(sampleContentValid)) + err := contentStore.Save(token, Collection, []byte(sampleContentValid)) require.NoError(t, err) // remove @@ -566,11 +555,10 @@ func TestContentStore_GetAll(t *testing.T) { t.Run("get all content from store for credential type - success", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) // save test data const count = 5 @@ -604,14 +592,13 @@ func TestContentStore_GetAll(t *testing.T) { sp := getMockStorageProvider() // wallet locked - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) allVcs, err := contentStore.GetAll(token, Credential) require.True(t, errors.Is(err, ErrWalletLocked)) require.Empty(t, allVcs) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) require.NoError(t, contentStore.Save(token, Credential, []byte(fmt.Sprintf(vcContent, uuid.New().String())))) // iterator value error @@ -624,10 +611,9 @@ func TestContentStore_GetAll(t *testing.T) { // iterator value error sp.MockStoreProvider.Store.ErrKey = errors.New(sampleContenttErr + uuid.New().String()) - contentStore, err = newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore = newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) allVcs, err = contentStore.GetAll(token, Credential) require.True(t, errors.Is(err, sp.MockStoreProvider.Store.ErrKey)) @@ -636,10 +622,9 @@ func TestContentStore_GetAll(t *testing.T) { // iterator next error sp.MockStoreProvider.Store.ErrNext = errors.New(sampleContenttErr + uuid.New().String()) - contentStore, err = newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore = newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) require.NoError(t, contentStore.Save(token, Credential, []byte(fmt.Sprintf(vcContent, uuid.New().String())))) @@ -650,10 +635,9 @@ func TestContentStore_GetAll(t *testing.T) { // iterator next error sp.MockStoreProvider.Store.ErrQuery = errors.New(sampleContenttErr + uuid.New().String()) - contentStore, err = newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore = newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) allVcs, err = contentStore.GetAll(token, Credential) require.True(t, errors.Is(err, sp.MockStoreProvider.Store.ErrQuery)) @@ -669,13 +653,12 @@ func TestContentDIDResolver(t *testing.T) { t.Run("create new content store - success", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) // save custom DID - err = contentStore.Save(token, DIDResolutionResponse, []byte(sampleDocResolutionResponse)) + err := contentStore.Save(token, DIDResolutionResponse, []byte(sampleDocResolutionResponse)) require.NoError(t, err) contentVDR := newContentBasedVDR(token, &vdr.MockVDRegistry{}, contentStore) @@ -696,8 +679,7 @@ func TestContentDIDResolver(t *testing.T) { t.Run("create new content store - errors", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) contentVDR := newContentBasedVDR(token, &vdr.MockVDRegistry{}, contentStore) @@ -709,7 +691,7 @@ func TestContentDIDResolver(t *testing.T) { require.Empty(t, didDoc) // open store - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) // DID not found didDoc, err = contentVDR.Resolve("did:key:invalid") @@ -794,10 +776,9 @@ func TestContentStore_Collections(t *testing.T) { t.Run("contents by collection - success", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) // save a collection require.NoError(t, contentStore.Save(token, Collection, []byte(orgCollection))) @@ -861,12 +842,11 @@ func TestContentStore_Collections(t *testing.T) { t.Run("contents by collection - failure", func(t *testing.T) { sp := getMockStorageProvider() - contentStore, err := newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore := newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) - err = contentStore.Save(token, + err := contentStore.Save(token, DIDResolutionResponse, []byte(didResolutionResult), AddByCollection(collectionID+"invalid")) require.Error(t, err) require.Contains(t, err.Error(), "failed to find existing collection") @@ -884,10 +864,9 @@ func TestContentStore_Collections(t *testing.T) { // get content error sp.MockStoreProvider.Store.ErrGet = errors.New(sampleContenttErr + uuid.New().String()) - contentStore, err = newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore = newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) allVcs, err := contentStore.GetAllByCollection(token, collectionID, Credential) require.True(t, errors.Is(err, sp.MockStoreProvider.Store.ErrGet)) @@ -896,10 +875,9 @@ func TestContentStore_Collections(t *testing.T) { // iterator value error sp.MockStoreProvider.Store.ErrValue = errors.New(sampleContenttErr + uuid.New().String()) - contentStore, err = newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore = newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) allVcs, err = contentStore.GetAllByCollection(token, collectionID, Credential) require.True(t, errors.Is(err, sp.MockStoreProvider.Store.ErrValue)) @@ -908,10 +886,9 @@ func TestContentStore_Collections(t *testing.T) { // iterator value error sp.MockStoreProvider.Store.ErrKey = errors.New(sampleContenttErr + uuid.New().String()) - contentStore, err = newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore = newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) allVcs, err = contentStore.GetAllByCollection(token, collectionID, Credential) require.True(t, errors.Is(err, sp.MockStoreProvider.Store.ErrKey)) @@ -920,10 +897,9 @@ func TestContentStore_Collections(t *testing.T) { // iterator next error sp.MockStoreProvider.Store.ErrNext = errors.New(sampleContenttErr + uuid.New().String()) - contentStore, err = newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore = newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) allVcs, err = contentStore.GetAllByCollection(token, collectionID, Credential) require.True(t, errors.Is(err, sp.MockStoreProvider.Store.ErrNext)) @@ -932,10 +908,9 @@ func TestContentStore_Collections(t *testing.T) { // iterator next error sp.MockStoreProvider.Store.ErrQuery = errors.New(sampleContenttErr + uuid.New().String()) - contentStore, err = newContentStore(sp, &profile{ID: uuid.New().String()}) - require.NoError(t, err) + contentStore = newContentStore(sp, &profile{ID: uuid.New().String()}) require.NotEmpty(t, contentStore) - require.NoError(t, contentStore.Open()) + require.NoError(t, contentStore.Open(token)) allVcs, err = contentStore.GetAllByCollection(token, collectionID, Credential) require.True(t, errors.Is(err, sp.MockStoreProvider.Store.ErrQuery)) diff --git a/pkg/wallet/kmsclient_test.go b/pkg/wallet/kmsclient_test.go index aa2313932c..f987612a19 100644 --- a/pkg/wallet/kmsclient_test.go +++ b/pkg/wallet/kmsclient_test.go @@ -10,12 +10,14 @@ import ( "crypto/sha256" "errors" "testing" + "time" "github.com/google/uuid" "github.com/stretchr/testify/require" kmsapi "github.com/hyperledger/aries-framework-go/pkg/kms" - "github.com/hyperledger/aries-framework-go/pkg/mock/kms" + mockcrypto "github.com/hyperledger/aries-framework-go/pkg/mock/crypto" + mockkms "github.com/hyperledger/aries-framework-go/pkg/mock/kms" mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage" "github.com/hyperledger/aries-framework-go/pkg/secretlock/local/masterlock/pbkdf2" ) @@ -556,7 +558,7 @@ func TestImportKeyBase58(t *testing.T) { sampleErr := errors.New(sampleKeyMgrErr) wkmgr := keyManager() err := wkmgr.saveKeyManger(uuid.New().String(), mockToken, - &kms.KeyManager{ImportPrivateKeyErr: sampleErr}, 0) + &mockkms.KeyManager{ImportPrivateKeyErr: sampleErr}, 0) require.NoError(t, err) err = importKeyBase58(mockToken, &keyContent{ @@ -576,3 +578,34 @@ func TestImportKeyBase58(t *testing.T) { require.True(t, errors.Is(err, sampleErr)) }) } + +func TestKMSSigner(t *testing.T) { + token := uuid.New().String() + + require.NoError(t, keyManager().saveKeyManger(uuid.New().String(), token, &mockkms.KeyManager{}, 500*time.Millisecond)) + + t.Run("test kms signer errors", func(t *testing.T) { + // invalid auth + signer, err := newKMSSigner("invalid", &mockcrypto.Crypto{}, &ProofOptions{}) + require.True(t, errors.Is(err, ErrWalletLocked)) + require.Empty(t, signer) + + // invalid verification method + signer, err = newKMSSigner(token, &mockcrypto.Crypto{}, &ProofOptions{ + VerificationMethod: "invalid", + }) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid verification method format") + require.Empty(t, signer) + + // sign error + signer, err = newKMSSigner(token, &mockcrypto.Crypto{SignErr: errors.New(sampleKeyMgrErr)}, &ProofOptions{ + VerificationMethod: "did:example#123", + }) + require.NoError(t, err) + + res, err := signer.Sign([]byte("1234")) + require.Error(t, err) + require.Empty(t, res) + }) +} diff --git a/pkg/wallet/options.go b/pkg/wallet/options.go index 9e616b2b23..f5c9de9daa 100644 --- a/pkg/wallet/options.go +++ b/pkg/wallet/options.go @@ -24,8 +24,7 @@ type profileOpts struct { keyServerURL string // EDV options - edvServerURL string - vaultID string + edvConf *edvConf } // ProfileOptions is option for verifiable credential wallet key manager. @@ -53,6 +52,20 @@ func WithKeyServerURL(url string) ProfileOptions { } } +// WithEDVStorage option, for wallet profile to use EDV as storage. +// If provided then all wallet contents will use EDV for storage. +// Note: key manager options supplied for profile creation and management will be reused for EDV operations. +func WithEDVStorage(url, vaultID, encryptionKID, macKID string) ProfileOptions { + return func(opts *profileOpts) { + opts.edvConf = &edvConf{ + ServerURL: url, + VaultID: vaultID, + EncryptionKeyID: encryptionKID, + MACKeyID: macKID, + } + } +} + // unlockOpts contains options for unlocking VC wallet client. type unlockOpts struct { // local kms options diff --git a/pkg/wallet/profile.go b/pkg/wallet/profile.go index 287550e086..2fa3d0cd4e 100644 --- a/pkg/wallet/profile.go +++ b/pkg/wallet/profile.go @@ -13,6 +13,7 @@ import ( "github.com/google/uuid" + "github.com/hyperledger/aries-framework-go/pkg/kms" "github.com/hyperledger/aries-framework-go/pkg/secretlock" "github.com/hyperledger/aries-framework-go/spi/storage" ) @@ -39,11 +40,22 @@ type profile struct { // KeyServerURL for remotekms. KeyServerURL string - // EDVServerURL for encrypted data vault storage of wallet contents. - EDVServerURL string + // EDV configuration + EDVConf *edvConf +} + +type edvConf struct { + // ServerURL for encrypted data vault storage of wallet contents. + ServerURL string // VaultID for encrypted data vault storage of wallet contents. VaultID string + + // Key ID for encryption key for EDV. + EncryptionKeyID string + + // Key ID for MAC key for EDV. + MACKeyID string } // createProfile creates new verifiable credential wallet profile for given user and saves it in store. @@ -56,7 +68,10 @@ func createProfile(user string, opts *profileOpts) (*profile, error) { return nil, err } - profile.setEDVOptions(opts.edvServerURL, opts.vaultID) + err = profile.setEDVOptions(opts.edvConf) + if err != nil { + return nil, err + } return profile, nil } @@ -94,9 +109,64 @@ func (pr *profile) setKMSOptions(passphrase string, secretLockSvc secretlock.Ser return nil } -func (pr *profile) setEDVOptions(edvServerURL, vaultID string) { - pr.EDVServerURL = edvServerURL - pr.VaultID = vaultID +func (pr *profile) setEDVOptions(opts *edvConf) error { + // non EDV users. + if opts == nil { + return nil + } + + if opts.ServerURL == "" || opts.VaultID == "" || opts.EncryptionKeyID == "" || opts.MACKeyID == "" { + return errors.New("invalid EDV settings in profile") + } + + pr.EDVConf = opts + + return nil +} + +// nolint:gocyclo +func (pr *profile) setupEDVKeys(auth string, encryptionKeyType, macKeyType kms.KeyType) (bool, error) { + setupEncKey := pr.EDVConf != nil && pr.EDVConf.EncryptionKeyID == "" + setupMacKey := pr.EDVConf != nil && pr.EDVConf.MACKeyID == "" + + if !(setupEncKey || setupMacKey) { + return false, nil + } + + keyMgr, err := keyManager().getKeyManger(auth) + if err != nil { + return false, err + } + + // if encryption key is not yet setup yet then create one and set. + if setupEncKey { + if encryptionKeyType == "" { + encryptionKeyType = kms.NISTP256ECDHKWType + } + + kid, _, err := keyMgr.Create(encryptionKeyType) + if err != nil { + return false, err + } + + pr.EDVConf.EncryptionKeyID = kid + } + + // if MAC key is not yet setup yet then create one and set. + if setupMacKey { + if macKeyType == "" { + macKeyType = kms.HMACSHA256Tag256Type + } + + kid, _, err := keyMgr.Create(macKeyType) + if err != nil { + return false, err + } + + pr.EDVConf.MACKeyID = kid + } + + return true, nil } func (pr *profile) resetKMSOptions() { @@ -147,7 +217,7 @@ func (p *profileStore) get(user string) (*profile, error) { } // save saves profile into store, -// if argument 'override=true' then replaces existing profile for given user else returns error. +// if argument 'override=true' then replaces existing profile else returns error. func (p *profileStore) save(val *profile, override bool) error { if !override { profileBytes, _ := p.get(val.User) //nolint: errcheck diff --git a/pkg/wallet/profile_test.go b/pkg/wallet/profile_test.go index f0ba37b520..81073ab53c 100644 --- a/pkg/wallet/profile_test.go +++ b/pkg/wallet/profile_test.go @@ -7,11 +7,16 @@ SPDX-License-Identifier: Apache-2.0 package wallet import ( + "errors" "fmt" "testing" + "time" + "github.com/google/uuid" "github.com/stretchr/testify/require" + kmsapi "github.com/hyperledger/aries-framework-go/pkg/kms" + mockkms "github.com/hyperledger/aries-framework-go/pkg/mock/kms" "github.com/hyperledger/aries-framework-go/pkg/mock/secretlock" mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage" ) @@ -59,6 +64,30 @@ func TestCreateNewProfile(t *testing.T) { require.Equal(t, profile.MasterLockCipher, sampleMasterCipherText) }) + t.Run("test create new profile with EDV conf", func(t *testing.T) { + profile, err := createProfile(sampleProfileUser, + &profileOpts{ + passphrase: samplePassPhrase, secretLockSvc: nil, keyServerURL: sampleKeyServerURL, + edvConf: &edvConf{ + ServerURL: "sample-server-url", + VaultID: "sample-vault-ID", + EncryptionKeyID: "sample-enc-kid", + MACKeyID: "sample-mac-kid", + }, + }) + + require.NoError(t, err) + require.NotEmpty(t, profile) + require.NotEmpty(t, profile.ID) + require.Empty(t, profile.KeyServerURL, "") + require.NotEmpty(t, profile.MasterLockCipher) + require.NotEmpty(t, profile.EDVConf) + require.NotEmpty(t, profile.EDVConf.ServerURL) + require.NotEmpty(t, profile.EDVConf.VaultID) + require.NotEmpty(t, profile.EDVConf.EncryptionKeyID) + require.NotEmpty(t, profile.EDVConf.MACKeyID) + }) + t.Run("test create new profile failure", func(t *testing.T) { // invalid profile option profile, err := createProfile(sampleProfileUser, @@ -78,6 +107,66 @@ func TestCreateNewProfile(t *testing.T) { require.Error(t, err) require.Contains(t, err.Error(), "failed to create master lock from secret lock service provided") require.Contains(t, err.Error(), sampleCustomProfileErr) + + // invalid EDV settings + profile, err = createProfile(sampleProfileUser, + &profileOpts{ + passphrase: samplePassPhrase, secretLockSvc: nil, keyServerURL: sampleKeyServerURL, + edvConf: &edvConf{ + ServerURL: "sample-server-url", + }, + }) + require.Empty(t, profile) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid EDV settings in profile") + }) + + t.Run("test setup edv keys", func(t *testing.T) { + sampleProfile := &profile{ + EDVConf: &edvConf{ + ServerURL: "sample-server-url", + VaultID: "sample-vault-id", + }, + } + + token := uuid.New().String() + kid := "sample-kid" + kms := &mockkms.KeyManager{CreateKeyID: kid} + + require.NoError(t, keyManager().saveKeyManger(uuid.New().String(), token, kms, 500*time.Millisecond)) + + // setup edv keys + ok, err := sampleProfile.setupEDVKeys(token, kmsapi.NISTP256ECDHKWType, kmsapi.HMACSHA256Tag256Type) + require.NoError(t, err) + require.True(t, ok) + require.Equal(t, sampleProfile.EDVConf.EncryptionKeyID, kid) + require.Equal(t, sampleProfile.EDVConf.MACKeyID, kid) + + // no update + ok, err = sampleProfile.setupEDVKeys(token, "", "") + require.NoError(t, err) + require.False(t, ok) + + // test create key error + require.NoError(t, keyManager().saveKeyManger(uuid.New().String(), token, + &mockkms.KeyManager{CreateKeyErr: errors.New(sampleKeyMgrErr)}, 500*time.Millisecond)) + + sampleProfile.EDVConf.MACKeyID = "" + ok, err = sampleProfile.setupEDVKeys(token, "", "") + require.Error(t, err) + require.Contains(t, err.Error(), sampleKeyMgrErr) + require.False(t, ok) + + sampleProfile.EDVConf.EncryptionKeyID = "" + ok, err = sampleProfile.setupEDVKeys(token, "", "") + require.Error(t, err) + require.Contains(t, err.Error(), sampleKeyMgrErr) + require.False(t, ok) + + // invalid auth + ok, err = sampleProfile.setupEDVKeys(token+"invalid", "", "") + require.Error(t, err) + require.False(t, ok) }) } diff --git a/pkg/wallet/storage.go b/pkg/wallet/storage.go new file mode 100644 index 0000000000..1761f0770c --- /dev/null +++ b/pkg/wallet/storage.go @@ -0,0 +1,139 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +//nolint +package wallet + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/bluele/gcache" + + "github.com/hyperledger/aries-framework-go/component/storage/edv" + "github.com/hyperledger/aries-framework-go/pkg/crypto" + "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto" + "github.com/hyperledger/aries-framework-go/pkg/didcomm/packer" + "github.com/hyperledger/aries-framework-go/pkg/doc/jose" + "github.com/hyperledger/aries-framework-go/pkg/kms" + "github.com/hyperledger/aries-framework-go/spi/storage" +) + +// storageProvider all wallet content store storage provider operations. +// if profile has EDV settings then call to OpenStore will return EDV store instance by creating new EDV provider. +// Otherwise default provider will be used to open store. +// (Refer #2745 for more details) +type storageProvider struct { + profile *profile + defaultProvider storage.Provider +} + +func newWalletStorageProvider(profile *profile, provider storage.Provider) *storageProvider { + return &storageProvider{profile: profile, defaultProvider: provider} +} + +// OpenStore opens and returns store and sets store config to provider. +// if wallet profile has EDV settings then auth provided will be used to initialize edv storage provider. +func (s *storageProvider) OpenStore(auth string, config storage.StoreConfiguration) (storage.Store, error) { + var provider storage.Provider + var err error + + if s.profile.EDVConf != nil { + provider, err = createEDVStorageProvider(auth, s.profile.EDVConf) + if err != nil { + return nil, err + } + } else { + provider = s.defaultProvider + } + + store, err := provider.OpenStore(s.profile.ID) + if err != nil { + return nil, fmt.Errorf("failed to open store : %w", err) + } + + err = provider.SetStoreConfig(s.profile.ID, config) + if err != nil { + e := store.Close() + if e != nil { + logger.Warnf("failed to close store: %s", e) + } + + return nil, fmt.Errorf("failed to set store config: %w", err) + } + + return store, nil +} + +func createEDVStorageProvider(auth string, conf *edvConf) (storage.Provider, error) { + // get key manager + keyMgr, err := keyManager().getKeyManger(auth) + if err != nil { + if errors.Is(err, gcache.KeyNotFoundError) { + return nil, ErrWalletLocked + } + + return nil, err + } + + // get crypto + cryptoImpl, err := tinkcrypto.New() + if err != nil { + return nil, fmt.Errorf("failed to create crypto: %w", err) + } + + // get jwe encrypter + jweEncrypter, err := getJWSEncrypter(conf.EncryptionKeyID, keyMgr, cryptoImpl) + if err != nil { + return nil, fmt.Errorf("failed to create JWE encrypter: %w", err) + } + + // get jwe decrypter + jweDecrypter := jose.NewJWEDecrypt(nil, cryptoImpl, keyMgr) + + // get MAC crypto + macCrypto, err := getMacCrypto(conf.MACKeyID, keyMgr, cryptoImpl) + if err != nil { + return nil, fmt.Errorf("failed to create mac crypto: %w", err) + } + + // create EDV provider + // TODO add zcap header support + return edv.NewRESTProvider(conf.ServerURL, conf.VaultID, + edv.NewEncryptedFormatter(jweEncrypter, jweDecrypter, macCrypto, edv.WithDeterministicDocumentIDs()), + edv.WithFullDocumentsReturnedFromQueries(), edv.WithBatchEndpointExtension()), nil +} + +// getJWSEncrypter creates and returns jwe encrypter based on key manager & crypto provided +func getJWSEncrypter(kid string, keyMgr kms.KeyManager, cryptoImpl crypto.Crypto) (*jose.JWEEncrypt, error) { + pubKeyBytes, err := keyMgr.ExportPubKeyBytes(kid) + if err != nil { + return nil, err + } + + ecPubKey := new(crypto.PublicKey) + + ecPubKey.KID = kid + + err = json.Unmarshal(pubKeyBytes, ecPubKey) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal JWE public key bytes to an EC public key: %w", err) + } + + return jose.NewJWEEncrypt(jose.A256GCM, packer.EnvelopeEncodingTypeV2, "", "", nil, + []*crypto.PublicKey{ecPubKey}, cryptoImpl) +} + +// getMacCrypto creates and returns MAC crypto based on key manager & crypto provided +func getMacCrypto(kid string, keyMgr kms.KeyManager, cryptoImpl crypto.Crypto) (*edv.MACCrypto, error) { + keyHandle, err := keyMgr.Get(kid) + if err != nil { + return nil, err + } + + return edv.NewMACCrypto(keyHandle, cryptoImpl), nil +} diff --git a/pkg/wallet/storage_test.go b/pkg/wallet/storage_test.go new file mode 100644 index 0000000000..d3f4d5dcac --- /dev/null +++ b/pkg/wallet/storage_test.go @@ -0,0 +1,120 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package wallet + +import ( + "errors" + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + + mockcrypto "github.com/hyperledger/aries-framework-go/pkg/mock/crypto" + mockkms "github.com/hyperledger/aries-framework-go/pkg/mock/kms" + "github.com/hyperledger/aries-framework-go/spi/storage" +) + +func TestStorageProvider_OpenStore(t *testing.T) { + sampleUser := uuid.New().String() + masterLock, err := getDefaultSecretLock(samplePassPhrase) + require.NoError(t, err) + + masterLockCipherText, err := createMasterLock(masterLock) + require.NoError(t, err) + require.NotEmpty(t, masterLockCipherText) + + profileInfo := &profile{ + User: sampleUser, + MasterLockCipher: masterLockCipherText, + } + + token, err := keyManager().createKeyManager(profileInfo, getMockStorageProvider(), + &unlockOpts{passphrase: samplePassPhrase}) + require.NoError(t, err) + require.NotEmpty(t, token) + + t.Run("successfully open store", func(t *testing.T) { + sp := getMockStorageProvider() + sampleProfile := &profile{ID: uuid.New().String(), User: uuid.New().String()} + wsp := newWalletStorageProvider(sampleProfile, sp) + + store, err := wsp.OpenStore(token, storage.StoreConfiguration{TagNames: []string{Credential.Name()}}) + require.NoError(t, err) + require.NotEmpty(t, store) + require.Len(t, sp.config.TagNames, 1) + }) + + t.Run("successfully open EDV store", func(t *testing.T) { + sampleProfile := &profile{ID: uuid.New().String(), User: uuid.New().String(), EDVConf: &edvConf{ + ServerURL: "sample-server", + VaultID: "sample-vault-ID", + }} + + ok, err := sampleProfile.setupEDVKeys(token, "", "") + require.NoError(t, err) + require.True(t, ok) + + wsp := newWalletStorageProvider(sampleProfile, nil) + + store, err := wsp.OpenStore(token, storage.StoreConfiguration{TagNames: []string{Credential.Name()}}) + require.NoError(t, err) + require.NotEmpty(t, store) + }) + + t.Run("failed to open EDV store", func(t *testing.T) { + sampleProfile := &profile{ID: uuid.New().String(), User: uuid.New().String(), EDVConf: &edvConf{ + ServerURL: "sample-server", + VaultID: "sample-vault-ID", + }} + + ok, err := sampleProfile.setupEDVKeys(token, "", "") + require.NoError(t, err) + require.True(t, ok) + + wsp := newWalletStorageProvider(sampleProfile, nil) + + // invalid auth + store, err := wsp.OpenStore(token+".", storage.StoreConfiguration{TagNames: []string{Credential.Name()}}) + require.Error(t, err) + require.True(t, errors.Is(err, ErrWalletLocked)) + require.Empty(t, store) + + // incorrect mac key ID + wsp.profile.EDVConf.MACKeyID += "x" + store, err = wsp.OpenStore(token, storage.StoreConfiguration{TagNames: []string{Credential.Name()}}) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to create mac crypto") + require.Empty(t, store) + + // incorrect encryption key ID + wsp.profile.EDVConf.EncryptionKeyID += "x" + store, err = wsp.OpenStore(token, storage.StoreConfiguration{TagNames: []string{Credential.Name()}}) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to create JWE encrypter") + require.Empty(t, store) + + val, err := getJWSEncrypter("sample-kid", &mockkms.KeyManager{ + ExportPubKeyBytesValue: []byte("invalid"), + }, &mockcrypto.Crypto{}) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to unmarshal JWE public key bytes to an EC public key") + require.Empty(t, val) + }) + + t.Run("failed to set store config", func(t *testing.T) { + sp := getMockStorageProvider() + sampleProfile := &profile{ID: uuid.New().String(), User: uuid.New().String()} + wsp := newWalletStorageProvider(sampleProfile, sp) + + sp.failure = errors.New(sampleWalletErr) + sp.Store.ErrClose = errors.New(sampleWalletErr) + + store, err := wsp.OpenStore(token, storage.StoreConfiguration{TagNames: []string{Credential.Name()}}) + require.Error(t, err) + require.Empty(t, store) + }) +} diff --git a/pkg/wallet/wallet.go b/pkg/wallet/wallet.go index a146512dfe..c689afb683 100644 --- a/pkg/wallet/wallet.go +++ b/pkg/wallet/wallet.go @@ -106,17 +106,12 @@ func New(userID string, ctx provider) (*Wallet, error) { return nil, fmt.Errorf("failed to get VC wallet profile: %w", err) } - contents, err := newContentStore(ctx.StorageProvider(), profile) - if err != nil { - return nil, fmt.Errorf("failed to get wallet content store: %w", err) - } - return &Wallet{ userID: userID, profile: profile, storeProvider: ctx.StorageProvider(), walletCrypto: ctx.Crypto(), - contents: contents, + contents: newContentStore(ctx.StorageProvider(), profile), vdr: ctx.VDRegistry(), jsonldDocumentLoader: ctx.JSONLDDocumentLoader(), }, nil @@ -152,6 +147,7 @@ func createOrUpdate(userID string, ctx provider, update bool, options ...Profile var profile *profile + // nolint: nestif if update { // find existing profile and update it. profile, err = store.get(userID) @@ -164,7 +160,10 @@ func createOrUpdate(userID string, ctx provider, update bool, options ...Profile return fmt.Errorf("failed to update wallet user profile KMS options: %w", err) } - profile.setEDVOptions(opts.edvServerURL, opts.vaultID) + err = profile.setEDVOptions(opts.edvConf) + if err != nil { + return fmt.Errorf("failed to update EDV configuration") + } } else { // create new profile. profile, err = createProfile(userID, opts) @@ -201,9 +200,8 @@ func (c *Wallet) Open(options ...UnlockOptions) (string, error) { return "", err } - // unlock content store - // TODO token to be passed to contents.Open() for EDV support - err = c.contents.Open() + // open content store using token + err = c.contents.Open(token) if err != nil { // close wallet if it fails to open store c.Close() diff --git a/pkg/wallet/wallet_test.go b/pkg/wallet/wallet_test.go index f661d1d627..955f6c412c 100644 --- a/pkg/wallet/wallet_test.go +++ b/pkg/wallet/wallet_test.go @@ -168,6 +168,10 @@ const ( didKeyBBS = "did:key:zUC72c7u4BYVmfYinDceXkNAwzPEyuEE23kUmJDjLy8495KH3pjLwFhae1Fww9qxxRdLnS2VNNwni6W3KbYZKsicDtiNNEp76fYWR6HCD8jAz6ihwmLRjcHH6kB294Xfg1SL1qQ" pkBBSBase58 = "6gsgGpdx7p1nYoKJ4b5fKt1xEomWdnemg9nJFX6mqNCh" keyIDBBS = "zUC72c7u4BYVmfYinDceXkNAwzPEyuEE23kUmJDjLy8495KH3pjLwFhae1Fww9qxxRdLnS2VNNwni6W3KbYZKsicDtiNNEp76fYWR6HCD8jAz6ihwmLRjcHH6kB294Xfg1SL1qQ" + sampleEDVServerURL = "sample-edv-url" + sampleEDVVaultID = "sample-edv-vault-id" + sampleEDVEncryptionKID = "sample-edv-encryption-kid" + sampleEDVMacKID = "sample-edv-mac-kid" ) func TestCreate(t *testing.T) { @@ -201,6 +205,22 @@ func TestCreate(t *testing.T) { require.NotEmpty(t, wallet) }) + t.Run("test create new wallet using remote kms key server URL & EDV", func(t *testing.T) { + mockctx := newMockProvider(t) + err := CreateProfile(sampleUserID, mockctx, WithKeyServerURL(sampleKeyServerURL), + WithEDVStorage(sampleEDVServerURL, sampleEDVVaultID, sampleEDVEncryptionKID, sampleEDVMacKID)) + require.NoError(t, err) + + wallet, err := New(sampleUserID, mockctx) + require.NoError(t, err) + require.NotEmpty(t, wallet) + require.NotEmpty(t, wallet.profile.EDVConf) + require.Equal(t, wallet.profile.EDVConf.ServerURL, sampleEDVServerURL) + require.Equal(t, wallet.profile.EDVConf.VaultID, sampleEDVVaultID) + require.Equal(t, wallet.profile.EDVConf.EncryptionKeyID, sampleEDVEncryptionKID) + require.Equal(t, wallet.profile.EDVConf.MACKeyID, sampleEDVMacKID) + }) + t.Run("test create new wallet failure", func(t *testing.T) { mockctx := newMockProvider(t) err := CreateProfile(sampleUserID, mockctx) @@ -243,22 +263,6 @@ func TestCreate(t *testing.T) { require.Error(t, err) require.Empty(t, wallet) }) - - t.Run("test create new wallet failure - create content store error", func(t *testing.T) { - mockctx := newMockProvider(t) - mockctx.StorageProviderValue = &mockStorageProvider{ - MockStoreProvider: mockstorage.NewMockStoreProvider(), - failure: fmt.Errorf(sampleWalletErr), - } - - err := CreateProfile(sampleUserID, mockctx, WithKeyServerURL(sampleKeyServerURL)) - require.NoError(t, err) - - wallet, err := New(sampleUserID, mockctx) - require.Error(t, err) - require.Empty(t, wallet) - require.Contains(t, err.Error(), "failed to get wallet content store:") - }) } func TestUpdate(t *testing.T) { @@ -301,6 +305,24 @@ func TestUpdate(t *testing.T) { require.NotEmpty(t, wallet.profile.KeyServerURL) }) + t.Run("test update wallet profile edv settings", func(t *testing.T) { + mockctx := newMockProvider(t) + createSampleProfile(t, mockctx) + + err := UpdateProfile(sampleUserID, mockctx, WithKeyServerURL(sampleKeyServerURL), + WithEDVStorage(sampleEDVServerURL, sampleEDVVaultID, sampleEDVEncryptionKID, sampleEDVMacKID)) + require.NoError(t, err) + + wallet, err := New(sampleUserID, mockctx) + require.NoError(t, err) + require.NotEmpty(t, wallet) + require.NotEmpty(t, wallet.profile.EDVConf) + require.Equal(t, wallet.profile.EDVConf.ServerURL, sampleEDVServerURL) + require.Equal(t, wallet.profile.EDVConf.VaultID, sampleEDVVaultID) + require.Equal(t, wallet.profile.EDVConf.EncryptionKeyID, sampleEDVEncryptionKID) + require.Equal(t, wallet.profile.EDVConf.MACKeyID, sampleEDVMacKID) + }) + t.Run("test update wallet failure", func(t *testing.T) { mockctx := newMockProvider(t) createSampleProfile(t, mockctx) @@ -357,6 +379,16 @@ func TestUpdate(t *testing.T) { require.Empty(t, wallet.profile.KeyServerURL) require.NotEmpty(t, wallet.profile.MasterLockCipher) }) + + t.Run("test update wallet failure - save edv settings error", func(t *testing.T) { + mockctx := newMockProvider(t) + createSampleProfile(t, mockctx) + + err := UpdateProfile(sampleUserID, mockctx, WithKeyServerURL(sampleKeyServerURL), + WithEDVStorage(sampleEDVServerURL, "", sampleEDVEncryptionKID, sampleEDVMacKID)) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to update EDV configuration") + }) } func TestNew(t *testing.T) { @@ -499,10 +531,9 @@ func TestWallet_OpenClose(t *testing.T) { require.NotEmpty(t, wallet) // corrupt content store - wallet.contents, err = newContentStore(&mockstorage.MockStoreProvider{ + wallet.contents = newContentStore(&mockstorage.MockStoreProvider{ ErrOpenStoreHandle: fmt.Errorf(sampleWalletErr), }, wallet.profile) - require.NoError(t, err) // get token token, err := wallet.Open(WithUnlockByAuthorizationToken(sampleRemoteKMSAuth)) @@ -949,6 +980,35 @@ func TestWallet_Issue(t *testing.T) { require.Len(t, result.Proofs, 1) }) + t.Run("Test VC wallet issue JSONWebSignature2020 using controller - success", func(t *testing.T) { + walletInstance, err := New(user, mockctx) + require.NotEmpty(t, walletInstance) + require.NoError(t, err) + + // unlock wallet + authToken, err := walletInstance.Open(WithUnlockByPassphrase(samplePassPhrase)) + require.NoError(t, err) + require.NotEmpty(t, authToken) + + defer walletInstance.Close() + + // import keys manually + kmgr, err := keyManager().getKeyManger(authToken) + require.NoError(t, err) + edPriv := ed25519.PrivateKey(base58.Decode(pkBase58)) + // nolint: errcheck, gosec + kmgr.ImportPrivateKey(edPriv, kms.ED25519, kms.WithKeyID(kid)) + + // sign with just controller + result, err := walletInstance.Issue(authToken, []byte(sampleUDCVC), &ProofOptions{ + Controller: didKey, + ProofType: JSONWebSignature2020, + }) + require.NoError(t, err) + require.NotEmpty(t, result) + require.Len(t, result.Proofs, 1) + }) + t.Run("Test VC wallet issue using verification method - success", func(t *testing.T) { walletInstance, err := New(user, mockctx) require.NotEmpty(t, walletInstance)