diff --git a/go.mod b/go.mod index 9f0fa8826937a5ffa41bf934cbb6ab8c398064c3..972eabec2f74a0a8d68db051612aeeac9cce1b6c 100644 --- a/go.mod +++ b/go.mod @@ -7,11 +7,11 @@ require ( github.com/hack-pad/go-indexeddb v0.2.0 github.com/pkg/errors v0.9.1 github.com/spf13/jwalterweatherman v1.1.0 - gitlab.com/elixxir/client v1.5.1-0.20221022003714-8980452f25e5 - gitlab.com/elixxir/crypto v0.0.7-0.20221022003355-d8a6158b32a7 + gitlab.com/elixxir/client v1.5.1-0.20221024210920-177a44943bef + gitlab.com/elixxir/crypto v0.0.7-0.20221024012326-cf941c375c1f gitlab.com/elixxir/primitives v0.0.3-0.20221017172918-6176818d1aba gitlab.com/xx_network/crypto v0.0.5-0.20221017172404-b384a8d8b171 - gitlab.com/xx_network/primitives v0.0.4-0.20221017171439-42169a3e5c0d + gitlab.com/xx_network/primitives v0.0.4-0.20221020224054-cab1210c4841 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa ) @@ -23,26 +23,40 @@ require ( github.com/cloudflare/circl v1.2.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/elliotchance/orderedmap v1.4.0 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/gorilla/websocket v1.5.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/improbable-eng/grpc-web v0.15.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.11.7 // indirect github.com/klauspost/cpuid/v2 v2.1.0 // indirect + github.com/magiconair/properties v1.8.6 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pelletier/go-toml/v2 v2.0.2 // indirect + github.com/pkg/profile v1.6.0 // indirect github.com/rs/cors v1.8.2 // indirect github.com/sethvargo/go-diceware v0.3.0 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/soheilhy/cmux v0.1.5 // indirect + github.com/spf13/afero v1.9.2 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/cobra v1.5.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.12.0 // indirect + github.com/subosito/gotenv v1.4.0 // indirect github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect github.com/ttacon/libphonenumber v1.2.1 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/zeebo/blake3 v0.2.3 // indirect gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f // indirect - gitlab.com/elixxir/comms v0.0.4-0.20221017173926-4eaa6061dfaa // indirect + gitlab.com/elixxir/comms v0.0.4-0.20221024050701-bced94c1b026 // indirect gitlab.com/elixxir/ekv v0.2.1 // indirect gitlab.com/xx_network/comms v0.0.4-0.20221017172508-09e33697dc15 // indirect gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93 // indirect @@ -54,5 +68,8 @@ require ( google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect google.golang.org/grpc v1.49.0 // indirect google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/ini.v1 v1.66.6 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect nhooyr.io/websocket v1.8.7 // indirect ) diff --git a/go.sum b/go.sum index 9ac20560b7d4e69c4029a5a9b2627379b5d6c9e0..a828bf7d433d006ba57b34dd6bdbb88e6eb018ce 100644 --- a/go.sum +++ b/go.sum @@ -165,6 +165,7 @@ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2 github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -330,6 +331,7 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -347,6 +349,7 @@ github.com/improbable-eng/grpc-web v0.12.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6 github.com/improbable-eng/grpc-web v0.14.1/go.mod h1:zEjGHa8DAlkoOXmswrNvhUGEYQA9UI7DhrGeHR1DMGU= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -402,6 +405,7 @@ github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0U github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea h1:uyJ13zfy6l79CM3HnVhDalIyZ4RJAyVfDrbnfFeJoC4= github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea/go.mod h1:w4pGU9PkiX2hAWyF0yuHEHmYTQFAd6WHzp6+IY7JVjE= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -434,6 +438,7 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -479,8 +484,10 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= @@ -490,6 +497,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/profile v1.6.0 h1:hUDfIISABYI59DyeB3OTay/HxSRwTQ8rB/H83k6r5dM= github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -556,14 +564,19 @@ github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -582,6 +595,7 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= +github.com/subosito/gotenv v1.4.0 h1:yAzM1+SmVcz5R4tXGsNMu1jUl2aOJXoiWUCEwwnGrvs= github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7vWWGaGo= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 h1:5u+EJUQiosu3JFX0XS0qTf5FznsMOzTjGqavBGuCbo0= @@ -616,15 +630,18 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f h1:yXGvNBqzZwAhDYlSnxPRbgor6JWoOt1Z7s3z1O9JR40= gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k= -gitlab.com/elixxir/client v1.5.1-0.20221022003714-8980452f25e5 h1:8SsC+GZYTZBDMn47OpSBc1kRVvFpx0IYZG6B8sL9BzU= -gitlab.com/elixxir/client v1.5.1-0.20221022003714-8980452f25e5/go.mod h1:a5pLHWVKdwacbyL9yGlV/xP5xHbhC2ycqTbEsDyzjo4= -gitlab.com/elixxir/comms v0.0.4-0.20221017173926-4eaa6061dfaa h1:/FEpu0N0rAyq74FkvO3uY8BcQoWLSbVPhj/s5QfscZw= -gitlab.com/elixxir/comms v0.0.4-0.20221017173926-4eaa6061dfaa/go.mod h1:rW7xdbHntP2MoF3q+2+f+IR8OHol94MRyviotfR5rXg= +gitlab.com/elixxir/client v1.5.1-0.20221024201221-688c766ef5c7 h1:dhWRgVnk7+9tqWSc3QySyzdV6IPMcrmpEU+3gb8ivS4= +gitlab.com/elixxir/client v1.5.1-0.20221024201221-688c766ef5c7/go.mod h1:KVllKaLZKT9fLruj+0woZK5t8JKZNJTDJLsCNF+yIqQ= +gitlab.com/elixxir/client v1.5.1-0.20221024202334-80c87bc2908c h1:deQEaPPJrjkmy6ij8RzxLFm3hfBLu3iZT/y0vmzoYno= +gitlab.com/elixxir/client v1.5.1-0.20221024202334-80c87bc2908c/go.mod h1:KVllKaLZKT9fLruj+0woZK5t8JKZNJTDJLsCNF+yIqQ= +gitlab.com/elixxir/client v1.5.1-0.20221024210920-177a44943bef h1:ZrL1sZfNMblXZRcUYv1QkSskT50fiYhbo3nEXwzuI+w= +gitlab.com/elixxir/client v1.5.1-0.20221024210920-177a44943bef/go.mod h1:KVllKaLZKT9fLruj+0woZK5t8JKZNJTDJLsCNF+yIqQ= +gitlab.com/elixxir/comms v0.0.4-0.20221024050701-bced94c1b026 h1:CdqvzyM91wN6u4MmGj0n+gKO/0tJabWPN3EQ4SFsZsg= +gitlab.com/elixxir/comms v0.0.4-0.20221024050701-bced94c1b026/go.mod h1:NevrBdsi5wJvitUeMsid3xI1FrzzuzfxKy4Bapnhzao= gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c= gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA= -gitlab.com/elixxir/crypto v0.0.7-0.20221017173452-565da4101a3b/go.mod h1:1rftbwSVdy49LkBIkPr+w+P2mDOerYeBKoZuB3r0yqI= -gitlab.com/elixxir/crypto v0.0.7-0.20221022003355-d8a6158b32a7 h1:+8DHBxZxJcmJSmcUFK4ZjjXgwV3wSo9O4+4NCaLdO4c= -gitlab.com/elixxir/crypto v0.0.7-0.20221022003355-d8a6158b32a7/go.mod h1:P/S3pEPYl7fuHQ1m4mL2pIaCxAjYIXrJml/pnfofI+U= +gitlab.com/elixxir/crypto v0.0.7-0.20221024012326-cf941c375c1f h1:ku5gWZnvgs8TPHfGIOfKO5QPnRJl5fsSAic5H1Y/QRg= +gitlab.com/elixxir/crypto v0.0.7-0.20221024012326-cf941c375c1f/go.mod h1:P/S3pEPYl7fuHQ1m4mL2pIaCxAjYIXrJml/pnfofI+U= gitlab.com/elixxir/ekv v0.2.1 h1:dtwbt6KmAXG2Tik5d60iDz2fLhoFBgWwST03p7T+9Is= gitlab.com/elixxir/ekv v0.2.1/go.mod h1:USLD7xeDnuZEavygdrgzNEwZXeLQJK/w1a+htpN+JEU= gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg= @@ -643,8 +660,9 @@ gitlab.com/xx_network/crypto v0.0.5-0.20221017172404-b384a8d8b171/go.mod h1:sZ5b gitlab.com/xx_network/primitives v0.0.0-20200803231956-9b192c57ea7c/go.mod h1:wtdCMr7DPePz9qwctNoAUzZtbOSHSedcK++3Df3psjA= gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da/go.mod h1:OK9xevzWCaPO7b1wiluVJGk7R5ZsuC7pHY5hteZFQug= gitlab.com/xx_network/primitives v0.0.2/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc= -gitlab.com/xx_network/primitives v0.0.4-0.20221017171439-42169a3e5c0d h1:rOcGGx5SrhBkbD1P8JqbtqBBeuGhOSZxETytNGJcf/U= gitlab.com/xx_network/primitives v0.0.4-0.20221017171439-42169a3e5c0d/go.mod h1:AXVVFt7dDAeIUpOGPiStCcUIKsBXLWbmV/BgZ4T+tOo= +gitlab.com/xx_network/primitives v0.0.4-0.20221020224054-cab1210c4841 h1:dhctONxd94XP4wLye22EuZvQoeFNXXlkkE47VQSlbGs= +gitlab.com/xx_network/primitives v0.0.4-0.20221020224054-cab1210c4841/go.mod h1:AXVVFt7dDAeIUpOGPiStCcUIKsBXLWbmV/BgZ4T+tOo= gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93 h1:eJZrXqHsMmmejEPWw8gNAt0I8CGAMNO/7C339Zco3TM= gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -1208,6 +1226,7 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI= gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/indexedDb/implementation.go b/indexedDb/implementation.go index 410358d773fb486d31896dfe18fda0f8a29600b1..d9d2f7026d246f95a5e45def3863083805ae9434 100644 --- a/indexedDb/implementation.go +++ b/indexedDb/implementation.go @@ -39,6 +39,7 @@ const dbTimeout = time.Second // channel. type wasmModel struct { db *idb.Database + cipher cryptoChannel.Cipher receivedMessageCB MessageReceivedCallback updateMux sync.Mutex } @@ -206,6 +207,16 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID, timestamp time.Time, lease time.Duration, round rounds.Round, mType channels.MessageType, status channels.SentStatus) uint64 { + // Handle encryption, if it is present + if w.cipher != nil { + cipherText, err := w.cipher.Encrypt([]byte(text)) + if err != nil { + jww.ERROR.Printf("Failed to encrypt Message: %+v", err) + return 0 + } + text = string(cipherText) + } + msgToInsert := buildMessage( channelID.Marshal(), messageID.Bytes(), nil, nickname, text, pubKey, codeset, timestamp, lease, round.ID, mType, status) diff --git a/indexedDb/implementation_test.go b/indexedDb/implementation_test.go index 68a8dfeb6f04a8a1e1f9c141d9e07fa14f45019f..1de2f45703ac07ff1d1bebd7bdec959d006fe7ef 100644 --- a/indexedDb/implementation_test.go +++ b/indexedDb/implementation_test.go @@ -12,6 +12,8 @@ package indexedDb import ( "encoding/json" "fmt" + "github.com/hack-pad/go-indexeddb/idb" + "gitlab.com/elixxir/xxdk-wasm/storage" "gitlab.com/xx_network/primitives/netTime" "os" "strconv" @@ -37,7 +39,7 @@ func dummyCallback(uint64, *id.ID, bool) {} func Test_wasmModel_UpdateSentStatus(t *testing.T) { testString := "test" testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1}) - eventModel, err := newWASMModel(testString, dummyCallback) + eventModel, err := newWASMModel(testString, nil, dummyCallback) if err != nil { t.Fatalf("%+v", err) } @@ -90,7 +92,8 @@ func Test_wasmModel_UpdateSentStatus(t *testing.T) { // Smoke test wasmModel.JoinChannel/wasmModel.LeaveChannel happy paths. func Test_wasmModel_JoinChannel_LeaveChannel(t *testing.T) { - eventModel, err := newWASMModel("test", dummyCallback) + storage.GetLocalStorage().Clear() + eventModel, err := newWASMModel("test", nil, dummyCallback) if err != nil { t.Fatalf("%+v", err) } @@ -129,7 +132,7 @@ func Test_wasmModel_JoinChannel_LeaveChannel(t *testing.T) { // Test UUID gets returned when different messages are added. func Test_wasmModel_UUIDTest(t *testing.T) { testString := "testHello" - eventModel, err := newWASMModel(testString, dummyCallback) + eventModel, err := newWASMModel(testString, nil, dummyCallback) if err != nil { t.Fatalf("%+v", err) } @@ -162,8 +165,9 @@ func Test_wasmModel_UUIDTest(t *testing.T) { // Tests if the same message ID being sent always returns the same UUID. func Test_wasmModel_DuplicateReceives(t *testing.T) { + storage.GetLocalStorage().Clear() testString := "testHello" - eventModel, err := newWASMModel(testString, dummyCallback) + eventModel, err := newWASMModel(testString, nil, dummyCallback) if err != nil { t.Fatalf("%+v", err) } @@ -200,7 +204,7 @@ func Test_wasmModel_deleteMsgByChannel(t *testing.T) { testString := "test_deleteMsgByChannel" totalMessages := 10 expectedMessages := 5 - eventModel, err := newWASMModel(testString, dummyCallback) + eventModel, err := newWASMModel(testString, nil, dummyCallback) if err != nil { t.Fatalf("%+v", err) } @@ -249,3 +253,55 @@ func Test_wasmModel_deleteMsgByChannel(t *testing.T) { t.Errorf("Expected %d messages, got %d", expectedMessages, len(result)) } } + +// This test is designed to prove the behavior of unique indexes. +// Inserts will not fail, they simply will not happen. +func TestWasmModel_receiveHelper_UniqueIndex(t *testing.T) { + testString := "test_receiveHelper_UniqueIndex" + eventModel, err := newWASMModel(testString, nil, dummyCallback) + if err != nil { + t.Fatal(err) + } + + // Ensure index is unique + txn, err := eventModel.db.Transaction(idb.TransactionReadOnly, messageStoreName) + if err != nil { + t.Fatal(err) + } + store, err := txn.ObjectStore(messageStoreName) + if err != nil { + t.Fatal(err) + } + idx, err := store.Index(messageStoreMessageIndex) + if err != nil { + t.Fatal(err) + } + if isUnique, err := idx.Unique(); !isUnique { + t.Fatalf("Index is not unique!") + } else if err != nil { + t.Fatal(err) + } + + // First message insert should succeed + testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1}) + testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil, + testString, testString, []byte{8, 6, 7, 5}, 0, netTime.Now(), + time.Second, 0, 0, channels.Sent) + _, err = eventModel.receiveHelper(testMsg) + if err != nil { + t.Fatal(err) + } + + // The duplicate entry won't fail, it just silently shouldn't happen + _, err = eventModel.receiveHelper(testMsg) + if err != nil { + t.Fatalf("%+v", err) + } + results, err := eventModel.dump(messageStoreName) + if err != nil { + t.Fatalf("%+v", err) + } + if len(results) != 1 { + t.Fatalf("Expected only a single message, got %d", len(results)) + } +} diff --git a/indexedDb/init.go b/indexedDb/init.go index 651fb1e1c51a16202373065aa71e6a89e0d181e5..271300b4bafcec519a1b15ea05518fb0cfae5d6a 100644 --- a/indexedDb/init.go +++ b/indexedDb/init.go @@ -10,6 +10,8 @@ package indexedDb import ( + "github.com/pkg/errors" + cryptoChannel "gitlab.com/elixxir/crypto/channel" "gitlab.com/elixxir/xxdk-wasm/storage" "syscall/js" @@ -38,25 +40,25 @@ type MessageReceivedCallback func(uuid uint64, channelID *id.ID, update bool) // NewWASMEventModelBuilder returns an EventModelBuilder which allows // the channel manager to define the path but the callback is the same // across the board. -func NewWASMEventModelBuilder( +func NewWASMEventModelBuilder(encryption cryptoChannel.Cipher, cb MessageReceivedCallback) channels.EventModelBuilder { fn := func(path string) (channels.EventModel, error) { - return NewWASMEventModel(path, cb) + return NewWASMEventModel(path, encryption, cb) } return fn } // NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel. // The name should be a base64 encoding of the users public key. -func NewWASMEventModel(path string, cb MessageReceivedCallback) ( - channels.EventModel, error) { +func NewWASMEventModel(path string, encryption cryptoChannel.Cipher, + cb MessageReceivedCallback) (channels.EventModel, error) { databaseName := path + databaseSuffix - return newWASMModel(databaseName, cb) + return newWASMModel(databaseName, encryption, cb) } // newWASMModel creates the given [idb.Database] and returns a wasmModel. -func newWASMModel(databaseName string, cb MessageReceivedCallback) ( - *wasmModel, error) { +func newWASMModel(databaseName string, encryption cryptoChannel.Cipher, + cb MessageReceivedCallback) (*wasmModel, error) { // Attempt to open database object ctx, cancel := newContext() defer cancel() @@ -88,8 +90,45 @@ func newWASMModel(databaseName string, cb MessageReceivedCallback) ( // Wait for database open to finish db, err := openRequest.Await(ctx) + if err != nil { + return nil, err + } + + // FIXME: The below is a hack that for some reason prevents moving on with + // uninitialized database despite the previous call to Await. + // It would be idea to find a different solution. + // Close and open again to ensure the state is finalized + err = db.Close() + if err != nil { + return nil, err + } + openRequest, err = idb.Global().Open(ctx, databaseName, currentVersion, + func(db *idb.Database, oldVersion, newVersion uint) error { + return nil + }) + if err != nil { + return nil, err + } + // Wait for database open to finish + db, err = openRequest.Await(ctx) + if err != nil { + return nil, err + } - return &wasmModel{db: db, receivedMessageCB: cb}, err + encryptionStatus := encryption != nil + loadedEncryptionStatus, err := storage.StoreIndexedDbEncryptionStatus( + databaseName, encryptionStatus) + if err != nil { + return nil, err + } + + if encryptionStatus != loadedEncryptionStatus { + return nil, errors.New( + "Cannot load database with different encryption status.") + } else if !encryptionStatus { + jww.WARN.Printf("IndexedDb encryption disabled!") + } + return &wasmModel{db: db, receivedMessageCB: cb, cipher: encryption}, err } // v1Upgrade performs the v0 -> v1 database upgrade. diff --git a/main.go b/main.go index b1507bb7e21fe27331c35616718b5ef2c96e06d5..7c5f570cf47b27cad75d3e65ba1465bd0590667e 100644 --- a/main.go +++ b/main.go @@ -73,13 +73,21 @@ func main() { js.FuncOf(wasm.NewChannelsManagerWithIndexedDb)) js.Global().Set("LoadChannelsManagerWithIndexedDb", js.FuncOf(wasm.LoadChannelsManagerWithIndexedDb)) + js.Global().Set("LoadChannelsManagerWithIndexedDbUnsafe", + js.FuncOf(wasm.LoadChannelsManagerWithIndexedDbUnsafe)) + js.Global().Set("NewChannelsManagerWithIndexedDbUnsafe", + js.FuncOf(wasm.NewChannelsManagerWithIndexedDbUnsafe)) js.Global().Set("GenerateChannel", js.FuncOf(wasm.GenerateChannel)) + js.Global().Set("GetSavedChannelPrivateKeyUNSAFE", + js.FuncOf(wasm.GetSavedChannelPrivateKeyUNSAFE)) js.Global().Set("DecodePublicURL", js.FuncOf(wasm.DecodePublicURL)) js.Global().Set("DecodePrivateURL", js.FuncOf(wasm.DecodePrivateURL)) js.Global().Set("GetChannelJSON", js.FuncOf(wasm.GetChannelJSON)) js.Global().Set("GetChannelInfo", js.FuncOf(wasm.GetChannelInfo)) js.Global().Set("GetShareUrlType", js.FuncOf(wasm.GetShareUrlType)) js.Global().Set("IsNicknameValid", js.FuncOf(wasm.IsNicknameValid)) + js.Global().Set("NewChannelsDatabaseCipher", + js.FuncOf(wasm.NewChannelsDatabaseCipher)) // wasm/cmix.go js.Global().Set("NewCmix", js.FuncOf(wasm.NewCmix)) diff --git a/storage/indexedDbEncryptionTrack.go b/storage/indexedDbEncryptionTrack.go new file mode 100644 index 0000000000000000000000000000000000000000..6e52a2648d3ea076578e9697fa3e9b591edf043f --- /dev/null +++ b/storage/indexedDbEncryptionTrack.go @@ -0,0 +1,37 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +//go:build js && wasm + +package storage + +import ( + "github.com/pkg/errors" + "os" +) + +// Key to store if the database is encrypted or not +const databaseEncryptionToggleKey = "xxdkWasmDatabaseEncryptionToggle/" + +// StoreIndexedDbEncryptionStatus stores the encryption status if it has not +// been previously saved. If it has, it returns its value. +func StoreIndexedDbEncryptionStatus( + databaseName string, encryption bool) (bool, error) { + data, err := GetLocalStorage().GetItem( + databaseEncryptionToggleKey + databaseName) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + GetLocalStorage().SetItem( + databaseEncryptionToggleKey+databaseName, []byte{1}) + return encryption, nil + } else { + return false, err + } + } + + return data[0] == 1, nil +} diff --git a/storage/version.go b/storage/version.go index 263ad6a9c4c17762d84b6229f50b5c371fea2e65..e667145c9260470e6d367ddb75f952f17b714363 100644 --- a/storage/version.go +++ b/storage/version.go @@ -17,7 +17,7 @@ import ( ) // SEMVER is the current semantic version of xxDK WASM. -const SEMVER = "0.0.0" +const SEMVER = "0.1.0" // Storage keys. const ( diff --git a/wasm/channels.go b/wasm/channels.go index ebb8a4119e2d7a12891f3392d430540dcd9b096f..dc68f919fef27270f4479c44db549182d1d942a1 100644 --- a/wasm/channels.go +++ b/wasm/channels.go @@ -94,7 +94,7 @@ func GenerateChannelIdentity(_ js.Value, args []js.Value) interface{} { return utils.CopyBytesToJS(pi) } -// identityMap stores identifies previously generated by ConstructIdentity. +// identityMap stores identities previously generated by ConstructIdentity. var identityMap sync.Map // ConstructIdentity constructs a [channel.Identity] from a user's public key @@ -325,21 +325,72 @@ func LoadChannelsManager(_ js.Value, args []js.Value) interface{} { // returned as an int and the channelID as a Uint8Array. The row in the // database that was updated can be found using the UUID. The channel ID is // provided so that the recipient can filter if they want to the processes +// the update now or not. An "update" bool is present which tells you if the +// row is new or if it is an edited old row. +// - args[3] - ID of [ChannelDbCipher] object in tracker (int). Create this +// object with [NewChannelsDatabaseCipher] and get its id with +// [ChannelDbCipher.GetID]. +// +// Returns a promise: +// - Resolves to a Javascript representation of the [ChannelsManager] object. +// - Rejected with an error if loading indexedDb or the manager fails. +// - Throws a TypeError if the cipher ID does not correspond to a cipher. +func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) interface{} { + cmixID := args[0].Int() + privateIdentity := utils.CopyBytesToGo(args[1]) + cipherID := args[3].Int() + + cipher, err := bindings.GetChannelDbCipherTrackerFromID(cipherID) + if err != nil { + utils.Throw(utils.TypeError, err) + } + + return newChannelsManagerWithIndexedDb(cmixID, privateIdentity, args[2], cipher) +} + +// NewChannelsManagerWithIndexedDbUnsafe creates a new [ChannelsManager] from a +// new private identity ([channel.PrivateIdentity]) and using indexedDb as a +// backend to manage the event model. However, the data is written in plain text +// and not encrypted. It is recommended that you do not use this in production. +// +// This is for creating a manager for an identity for the first time. For +// generating a new one channel identity, use [GenerateChannelIdentity]. To +// reload this channel manager, use [LoadChannelsManagerWithIndexedDbUnsafe], +// passing in the storage tag retrieved by [ChannelsManager.GetStorageTag]. +// +// This function initialises an indexedDb database. +// +// Parameters: +// - args[0] - ID of [Cmix] object in tracker (int). This can be retrieved +// using [Cmix.GetID]. +// - args[1] - Bytes of a private identity ([channel.PrivateIdentity]) that is +// generated by [GenerateChannelIdentity] (Uint8Array). +// - args[2] - Function that takes in the same parameters as +// [indexedDb.MessageReceivedCallback]. On the Javascript side, the UUID is +// returned as an int and the channelID as a Uint8Array. The row in the +// database that was updated can be found using the UUID. The channel ID is +// provided so that the recipient can filter if they want to the processes // the update now or not. An "update" bool is present which tells you if // the row is new or if it is an edited old row // // Returns a promise: // - Resolves to a Javascript representation of the [ChannelsManager] object. // - Rejected with an error if loading indexedDb or the manager fails. -func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) interface{} { +func NewChannelsManagerWithIndexedDbUnsafe(_ js.Value, args []js.Value) interface{} { cmixID := args[0].Int() privateIdentity := utils.CopyBytesToGo(args[1]) + return newChannelsManagerWithIndexedDb(cmixID, privateIdentity, args[2], nil) +} + +func newChannelsManagerWithIndexedDb(cmixID int, privateIdentity []byte, + cb js.Value, cipher *bindings.ChannelDbCipher) interface{} { + fn := func(uuid uint64, channelID *id.ID, update bool) { - args[2].Invoke(uuid, utils.CopyBytesToJS(channelID.Marshal()), update) + cb.Invoke(uuid, utils.CopyBytesToJS(channelID.Marshal()), update) } - model := indexedDb.NewWASMEventModelBuilder(fn) + model := indexedDb.NewWASMEventModelBuilder(cipher, fn) promiseFn := func(resolve, reject func(args ...interface{}) js.Value) { cm, err := bindings.NewChannelsManagerGoEventModel( @@ -372,21 +423,69 @@ func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) interface{} { // returned as an int and the channelID as a Uint8Array. The row in the // database that was updated can be found using the UUID. The channel ID is // provided so that the recipient can filter if they want to the processes +// the update now or not. An "update" bool is present which tells you if the +// row is new or if it is an edited old row. +// - args[3] - ID of [ChannelDbCipher] object in tracker (int). Create this +// object with [NewChannelsDatabaseCipher] and get its id with +// [ChannelDbCipher.GetID]. +// +// Returns a promise: +// - Resolves to a Javascript representation of the [ChannelsManager] object. +// - Rejected with an error if loading indexedDb or the manager fails. +// - Throws a TypeError if the cipher ID does not correspond to a cipher. +func LoadChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) interface{} { + cmixID := args[0].Int() + storageTag := args[1].String() + cipherID := args[3].Int() + + cipher, err := bindings.GetChannelDbCipherTrackerFromID(cipherID) + if err != nil { + utils.Throw(utils.TypeError, err) + } + + return loadChannelsManagerWithIndexedDb(cmixID, storageTag, args[2], cipher) +} + +// LoadChannelsManagerWithIndexedDbUnsafe loads an existing [ChannelsManager] +// using an existing indexedDb database as a backend to manage the event model. +// This should only be used to load unsafe channel managers created by +// [NewChannelsManagerWithIndexedDbUnsafe]. +// +// This is for loading a manager for an identity that has already been created. +// The channel manager should have previously been created with +// [NewChannelsManagerWithIndexedDb] and the storage is retrievable with +// [ChannelsManager.GetStorageTag]. +// +// Parameters: +// - args[0] - ID of [Cmix] object in tracker (int). This can be retrieved +// using [Cmix.GetID]. +// - args[1] - The storage tag associated with the previously created channel +// manager and retrieved with [ChannelsManager.GetStorageTag] (string). +// - args[2] - Function that takes in the same parameters as +// [indexedDb.MessageReceivedCallback]. On the Javascript side, the UUID is +// returned as an int and the channelID as a Uint8Array. The row in the +// database that was updated can be found using the UUID. The channel ID is +// provided so that the recipient can filter if they want to the processes // the update now or not. An "update" bool is present which tells you if // the row is new or if it is an edited old row // // Returns a promise: // - Resolves to a Javascript representation of the [ChannelsManager] object. // - Rejected with an error if loading indexedDb or the manager fails. -func LoadChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) interface{} { +func LoadChannelsManagerWithIndexedDbUnsafe(_ js.Value, args []js.Value) interface{} { cmixID := args[0].Int() storageTag := args[1].String() + return loadChannelsManagerWithIndexedDb(cmixID, storageTag, args[2], nil) +} + +func loadChannelsManagerWithIndexedDb(cmixID int, storageTag string, + cb js.Value, cipher *bindings.ChannelDbCipher) interface{} { fn := func(uuid uint64, channelID *id.ID, updated bool) { - args[2].Invoke(uuid, utils.CopyBytesToJS(channelID.Marshal()), updated) + cb.Invoke(uuid, utils.CopyBytesToJS(channelID.Marshal()), updated) } - model := indexedDb.NewWASMEventModelBuilder(fn) + model := indexedDb.NewWASMEventModelBuilder(cipher, fn) promiseFn := func(resolve, reject func(args ...interface{}) js.Value) { cm, err := bindings.LoadChannelsManagerGoEventModel( @@ -448,6 +547,29 @@ func GenerateChannel(_ js.Value, args []js.Value) interface{} { return utils.CopyBytesToJS(gen) } +// GetSavedChannelPrivateKeyUNSAFE loads the private key from storage for the +// given channel ID. +// +// NOTE: This function is unsafe and only for debugging purposes only. +// +// Parameters: +// - args[0] - ID of [Cmix] object in tracker (int). +// - args[1] - The [id.ID] of the channel in base 64 encoding (string). +// +// Returns: +// - The PEM file of the private key (string). +// - Throws a TypeError if retrieving the [Cmix] object or the private key +// fails. +func GetSavedChannelPrivateKeyUNSAFE(_ js.Value, args []js.Value) interface{} { + privKey, err := bindings.GetSavedChannelPrivateKeyUNSAFE(args[0].Int(), args[1].String()) + if err != nil { + utils.Throw(utils.TypeError, err) + return nil + } + + return privKey +} + // DecodePublicURL decodes the channel URL into a channel pretty print. This // function can only be used for public channel URLs. To get the privacy level // of a channel URL, use [GetShareUrlType]. @@ -517,7 +639,7 @@ func GetChannelJSON(_ js.Value, args []js.Value) interface{} { return nil } - return c + return utils.CopyBytesToJS(c) } // GetChannelInfo returns the info about a channel from its public description. @@ -547,7 +669,7 @@ func GetChannelInfo(_ js.Value, args []js.Value) interface{} { // // Parameters: // - args[0] - A portable channel string. Should be received from another user -// or generated via GenerateChannel (string). +// or generated via [GenerateChannel] (string). // // The pretty print will be of the format: // <Speakeasy-v2:Test_Channel|description:Channel description.|level:Public|secrets:+oHcqDbJPZaT3xD5NcdLY8OjOMtSQNKdKgLPmr7ugdU=|rCI0wr01dHFStjSFMvsBzFZClvDIrHLL5xbCOPaUOJ0=|493|1|7cBhJxVfQxWo+DypOISRpeWdQBhuQpAZtUbQHjBm8NQ=> @@ -1274,3 +1396,108 @@ func (em *eventModel) UpdateSentStatus( em.updateSentStatus( uuid, utils.CopyBytesToJS(messageID), timestamp, roundID, status) } + +//////////////////////////////////////////////////////////////////////////////// +// Channel Cipher // +//////////////////////////////////////////////////////////////////////////////// + +// ChannelDbCipher wraps the [bindings.ChannelDbCipher] object so its methods +// can be wrapped to be Javascript compatible. +type ChannelDbCipher struct { + api *bindings.ChannelDbCipher +} + +// newChannelDbCipherJS creates a new Javascript compatible object +// (map[string]interface{}) that matches the [ChannelDbCipher] structure. +func newChannelDbCipherJS(api *bindings.ChannelDbCipher) map[string]interface{} { + c := ChannelDbCipher{api} + channelDbCipherMap := map[string]interface{}{ + "GetID": js.FuncOf(c.GetID), + "Encrypt": js.FuncOf(c.Encrypt), + "Decrypt": js.FuncOf(c.Decrypt), + } + + return channelDbCipherMap +} + +// NewChannelsDatabaseCipher constructs a [ChannelDbCipher] object. +// +// Parameters: +// - args[0] - The tracked [Cmix] object ID (int). +// - args[1] - The password for storage. This should be the same password +// passed into [NewCmix] (Uint8Array). +// - args[2] - The maximum size of a payload to be encrypted. A payload passed +// into [ChannelDbCipher.Encrypt] that is larger than this value will result +// in an error (int). +// +// Returns: +// - JavaScript representation of the [ChannelDbCipher] object. +// - Throws a TypeError if creating the cipher fails. +func NewChannelsDatabaseCipher(_ js.Value, args []js.Value) interface{} { + cmixId := args[0].Int() + password := utils.CopyBytesToGo(args[1]) + plaintTextBlockSize := args[2].Int() + + cipher, err := bindings.NewChannelsDatabaseCipher( + cmixId, password, plaintTextBlockSize) + if err != nil { + utils.Throw(utils.TypeError, err) + return nil + } + + return newChannelDbCipherJS(cipher) +} + +// GetID returns the ID for this [bindings.ChannelDbCipher] in the +// channelDbCipherTracker. +// +// Returns: +// - Tracker ID (int). +func (c *ChannelDbCipher) GetID(js.Value, []js.Value) interface{} { + return c.api.GetID() +} + +// Encrypt will encrypt the raw data. It will return a ciphertext. Padding is +// done on the plaintext so all encrypted data looks uniform at rest. +// +// Parameters: +// - args[0] - The data to be encrypted (Uint8Array). This must be smaller than +// the block size passed into [NewChannelsDatabaseCipher]. If it is larger, +// this will return an error. +// +// Returns: +// - The ciphertext of the plaintext passed in (Uint8Array). +// - Throws a TypeError if it fails to encrypt the plaintext. +func (c *ChannelDbCipher) Encrypt(_ js.Value, args []js.Value) interface{} { + + ciphertext, err := c.api.Encrypt(utils.CopyBytesToGo(args[0])) + if err != nil { + utils.Throw(utils.TypeError, err) + return nil + } + + return utils.CopyBytesToJS(ciphertext) + +} + +// Decrypt will decrypt the passed in encrypted value. The plaintext will be +// returned by this function. Any padding will be discarded within this +// function. +// +// Parameters: +// - args[0] - the encrypted data returned by [ChannelDbCipher.Encrypt] +// (Uint8Array). +// +// Returns: +// - The plaintext of the ciphertext passed in (Uint8Array). +// - Throws a TypeError if it fails to encrypt the plaintext. +func (c *ChannelDbCipher) Decrypt(_ js.Value, args []js.Value) interface{} { + plaintext, err := c.api.Decrypt(utils.CopyBytesToGo(args[0])) + if err != nil { + utils.Throw(utils.TypeError, err) + return nil + } + + return utils.CopyBytesToJS(plaintext) + +} diff --git a/wasm/channels_test.go b/wasm/channels_test.go index 2e19cffdb62ef95ca5c3d2b70d726d619ba66797..127657af1507d6389bdf94e7fb920f7e1dddb2de 100644 --- a/wasm/channels_test.go +++ b/wasm/channels_test.go @@ -60,6 +60,47 @@ func Test_ChannelsManagerMethods(t *testing.T) { } } +// Tests that the map representing ChannelDbCipher returned by +// newChannelDbCipherJS contains all of the methods on ChannelDbCipher. +func Test_newChannelDbCipherJS(t *testing.T) { + cipherType := reflect.TypeOf(&ChannelDbCipher{}) + + cipher := newChannelDbCipherJS(&bindings.ChannelDbCipher{}) + if len(cipher) != cipherType.NumMethod() { + t.Errorf("ChannelDbCipher JS object does not have all methods."+ + "\nexpected: %d\nreceived: %d", cipherType.NumMethod(), len(cipher)) + } + + for i := 0; i < cipherType.NumMethod(); i++ { + method := cipherType.Method(i) + + if _, exists := cipher[method.Name]; !exists { + t.Errorf("Method %s does not exist.", method.Name) + } + } +} + +// Tests that ChannelDbCipher has all the methods that +// [bindings.ChannelDbCipher] has. +func Test_ChannelDbCipherMethods(t *testing.T) { + cipherType := reflect.TypeOf(&ChannelDbCipher{}) + binCipherType := reflect.TypeOf(&bindings.ChannelDbCipher{}) + + if binCipherType.NumMethod() != cipherType.NumMethod() { + t.Errorf("WASM ChannelDbCipher object does not have all methods from "+ + "bindings.\nexpected: %d\nreceived: %d", + binCipherType.NumMethod(), cipherType.NumMethod()) + } + + for i := 0; i < binCipherType.NumMethod(); i++ { + method := binCipherType.Method(i) + + if _, exists := cipherType.MethodByName(method.Name); !exists { + t.Errorf("Method %s does not exist.", method.Name) + } + } +} + type jsIdentity struct { pubKey js.Value codeset js.Value diff --git a/wasm_test.go b/wasm_test.go index 1b25d22270e75984c19ee41beeaf0e9a455df33e..38d1bdd30e9cd3efe0569afe50903b94948761af 100644 --- a/wasm_test.go +++ b/wasm_test.go @@ -42,6 +42,7 @@ func TestPublicFunctions(t *testing.T) { "NewEventModel": {}, "NewChannelsManagerGoEventModel": {}, "LoadChannelsManagerGoEventModel": {}, + "GetChannelDbCipherTrackerFromID": {}, // Version functions were renamed to differentiate between WASM and // client versions