diff --git a/.codeclimate.yml b/.codeclimate.yml index 71501aa0d..eed118ca8 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -42,4 +42,3 @@ exclude_patterns: - static/ - t/ - mailserver/migrations - - messaging/chat/migrations diff --git a/.golangci.yml b/.golangci.yml index 9153f013b..b24a070ba 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -47,7 +47,6 @@ linters: - golint - govet - ineffassign - - interfacer - megacheck - misspell - structcheck diff --git a/Makefile b/Makefile index f5b3f1ab9..a8f2b18aa 100644 --- a/Makefile +++ b/Makefile @@ -172,7 +172,6 @@ setup: setup-build setup-dev tidy ##@other Prepare project for development and b generate: ##@other Regenerate assets and other auto-generated stuff go generate ./static ./static/chat_db_migrations ./static/mailserver_db_migrations ./t - $(shell cd ./messaging/chat/protobuf && exec protoc --go_out=. ./*.proto) prepare-release: clean-release mkdir -p $(RELEASE_DIR) diff --git a/VERSION b/VERSION index ab907bf0b..442bd87ce 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.30.0-beta.3 +0.30.1-beta.0 diff --git a/api/backend.go b/api/backend.go index 76102740c..82b8c4060 100644 --- a/api/backend.go +++ b/api/backend.go @@ -18,8 +18,8 @@ import ( gethnode "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/status-im/status-go/account" + "github.com/status-im/status-go/crypto" "github.com/status-im/status-go/mailserver/registry" - "github.com/status-im/status-go/messaging/crypto" "github.com/status-im/status-go/node" "github.com/status-im/status-go/notifications/push/fcm" "github.com/status-im/status-go/params" diff --git a/crypto/crypto.go b/crypto/crypto.go new file mode 100644 index 000000000..361dbd4e1 --- /dev/null +++ b/crypto/crypto.go @@ -0,0 +1,81 @@ +package crypto + +import ( + "crypto/ecdsa" + "encoding/hex" + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/crypto" +) + +// Sign signs the hash of an arbitrary string +func Sign(content string, identity *ecdsa.PrivateKey) (string, error) { + signature, err := crypto.Sign(crypto.Keccak256([]byte(content)), identity) + if err != nil { + return "", err + } + + return hex.EncodeToString(signature), nil +} + +// VerifySignatures verifys tuples of signatures content/hash/public key +func VerifySignatures(signaturePairs [][3]string) error { + for _, signaturePair := range signaturePairs { + content := crypto.Keccak256([]byte(signaturePair[0])) + + signature, err := hex.DecodeString(signaturePair[1]) + if err != nil { + return err + } + + publicKeyBytes, err := hex.DecodeString(signaturePair[2]) + if err != nil { + return err + } + + publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes) + if err != nil { + return err + } + + recoveredKey, err := crypto.SigToPub( + content, + signature, + ) + if err != nil { + return err + } + + if crypto.PubkeyToAddress(*recoveredKey) != crypto.PubkeyToAddress(*publicKey) { + return errors.New("identity key and signature mismatch") + } + } + + return nil +} + +// ExtractSignatures extract from tuples of signatures content a public key +func ExtractSignatures(signaturePairs [][2]string) ([]string, error) { + response := make([]string, len(signaturePairs)) + for i, signaturePair := range signaturePairs { + content := crypto.Keccak256([]byte(signaturePair[0])) + + signature, err := hex.DecodeString(signaturePair[1]) + if err != nil { + return nil, err + } + + recoveredKey, err := crypto.SigToPub( + content, + signature, + ) + if err != nil { + return nil, err + } + + response[i] = fmt.Sprintf("%x", crypto.FromECDSAPub(recoveredKey)) + } + + return response, nil +} diff --git a/messaging/crypto/crypto_test.go b/crypto/crypto_test.go similarity index 81% rename from messaging/crypto/crypto_test.go rename to crypto/crypto_test.go index ae11f4ba2..5029a7fb2 100644 --- a/messaging/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -98,37 +98,3 @@ func TestVerifySignature(t *testing.T) { err = VerifySignatures(signaturePairs) require.Error(t, err) } - -func TestSymmetricEncryption(t *testing.T) { - const rawKey = "0000000000000000000000000000000000000000000000000000000000000000" - expectedPlaintext := []byte("test") - key, err := hex.DecodeString(rawKey) - require.Nil(t, err, "Key should be generated without errors") - - cyphertext1, err := EncryptSymmetric(key, expectedPlaintext) - require.Nil(t, err, "Cyphertext should be generated without errors") - - cyphertext2, err := EncryptSymmetric(key, expectedPlaintext) - require.Nil(t, err, "Cyphertext should be generated without errors") - - require.Equalf( - t, - 32, - len(cyphertext1), - "Cyphertext with the correct length should be generated") - - require.NotEqualf( - t, - cyphertext1, - cyphertext2, - "Same plaintext should not be encrypted in the same way") - - plaintext, err := DecryptSymmetric(key, cyphertext1) - require.Nil(t, err, "Cyphertext should be decrypted without errors") - - require.Equalf( - t, - expectedPlaintext, - plaintext, - "Cypther text should be decrypted successfully") -} diff --git a/messaging/crypto/ethereum_crypto.go b/crypto/ethereum_crypto.go similarity index 100% rename from messaging/crypto/ethereum_crypto.go rename to crypto/ethereum_crypto.go diff --git a/go.mod b/go.mod index e9481ec54..8307db8f2 100644 --- a/go.mod +++ b/go.mod @@ -8,33 +8,25 @@ require ( github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d github.com/ethereum/go-ethereum v1.8.27 - github.com/go-playground/locales v0.12.1 // indirect - github.com/go-playground/universal-translator v0.16.0 // indirect - github.com/golang-migrate/migrate v3.5.4+incompatible // indirect - github.com/golang-migrate/migrate/v4 v4.4.0 // indirect + github.com/golang-migrate/migrate/v4 v4.5.0 // indirect github.com/golang/mock v1.2.0 - github.com/golang/protobuf v1.3.1 github.com/lib/pq v1.0.0 github.com/libp2p/go-libp2p-core v0.0.3 github.com/multiformats/go-multiaddr v0.0.4 github.com/mutecomm/go-sqlcipher v0.0.0-20170920224653-f799951b4ab2 github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222 - github.com/russolsen/ohyeah v0.0.0-20160324131710-f4938c005315 // indirect - github.com/russolsen/same v0.0.0-20160222130632-f089df61f51d // indirect github.com/russolsen/transit v0.0.0-20180705123435-0794b4c4505a github.com/status-im/doubleratchet v2.0.0+incompatible github.com/status-im/migrate/v4 v4.3.1-status github.com/status-im/rendezvous v1.3.0 + github.com/status-im/status-protocol-go v0.1.1 github.com/status-im/whisper v1.4.14 github.com/stretchr/testify v1.3.0 github.com/syndtr/goleveldb v1.0.0 - go.uber.org/atomic v1.4.0 // indirect - go.uber.org/multierr v1.1.0 // indirect go.uber.org/zap v1.10.0 - golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 + golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 golang.org/x/text v0.3.2 - gopkg.in/go-playground/assert.v1 v1.2.1 // indirect - gopkg.in/go-playground/validator.v9 v9.9.3 + gopkg.in/go-playground/validator.v9 v9.29.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) diff --git a/go.sum b/go.sum index 34fbef8d2..4618216f2 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= github.com/aristanetworks/goarista v0.0.0-20190502180301-283422fc1708 h1:tS7jSmwRqSxTnonTRlDD1oHo6Q9YOK4xHS9/v4L56eg= github.com/aristanetworks/goarista v0.0.0-20190502180301-283422fc1708/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/aristanetworks/goarista v0.0.0-20190704150520-f44d68189fd7 h1:fKnuvQ/O22ZpD7HaJjGQXn/GxOdDJOQFL8bpM8Xe3X8= +github.com/aristanetworks/goarista v0.0.0-20190704150520-f44d68189fd7/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw= github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= @@ -75,6 +77,8 @@ 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/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X6GqbFowYdYdI0L9bwxL07jyPZIdepyZ0= github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= +github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= github.com/dgrijalva/jwt-go v0.0.0-20170201225849-2268707a8f08/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -130,6 +134,8 @@ github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBi github.com/golang-migrate/migrate v3.5.4+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk= github.com/golang-migrate/migrate/v4 v4.4.0 h1:bQVSVUJCZkAf6ci1J4aUNFO0FyXVoncZvHEFCVD2ezE= github.com/golang-migrate/migrate/v4 v4.4.0/go.mod h1:SzAcz2l+yDJVhQC7fwiF7T2MAFPMIkigJz98klRJ4OE= +github.com/golang-migrate/migrate/v4 v4.5.0 h1:ucd2qJu1BAKTtmjh7QlWYiq01DwlE/xXYQkOPE0ZTsI= +github.com/golang-migrate/migrate/v4 v4.5.0/go.mod h1:SzAcz2l+yDJVhQC7fwiF7T2MAFPMIkigJz98klRJ4OE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= @@ -234,6 +240,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kshvakov/clickhouse v1.3.5/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE= +github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= +github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libp2p/go-addr-util v0.0.1 h1:TpTQm9cXVRVSKsYbgQ7GKc3KbbHVTnbostgGaDEP+88= @@ -405,6 +413,8 @@ github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRr github.com/robertkrimen/otto v0.0.0-20170205013659-6a77b7cbc37d/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY= github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00 h1:8DPul/X0IT/1TNMIxoKLwdemEOBBHDC/K4EB16Cw5WE= github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= +github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521 h1:3hxavr+IHMsQBrYUPQM5v0CgENFktkkbg1sfpgM3h20= github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= github.com/russolsen/ohyeah v0.0.0-20160324131710-f4938c005315 h1:H3hCXwP92pH/hSgNrCLtjxvsKJ50sq26nICbZuoR1tQ= @@ -438,6 +448,9 @@ github.com/status-im/migrate/v4 v4.3.1-status h1:tJwsEYLgbFkvlTSMk89APwRDfpr4yG8 github.com/status-im/migrate/v4 v4.3.1-status/go.mod h1:r8HggRBZ/k7TRwByq/Hp3P/ubFppIna0nvyavVK0pjA= github.com/status-im/rendezvous v1.3.0 h1:7RK/MXXW+tlm0asKm1u7Qp7Yni6AO29a7j8+E4Lbjg4= github.com/status-im/rendezvous v1.3.0/go.mod h1:+hzjuP+j/XzLPeF6E50b88pWOTLdTcwjvNYt+Gh1W1s= +github.com/status-im/status-go v0.29.0-beta.3/go.mod h1:8OHekmRoYTn9oZgMsBdhMEgeyfaQ4eI4ycOFHJCoX7M= +github.com/status-im/status-protocol-go v0.1.1 h1:PGKtMTgfk48LS2ZyNIEdJZJjQGVbkBfccf6EVYCjCpA= +github.com/status-im/status-protocol-go v0.1.1/go.mod h1:rqlsXFR7WENTB4HDJRByRg95UfRo8pjGWvrvVrC2Dm4= github.com/status-im/whisper v1.4.14 h1:9VHqx4+PUYfhDnYYtDxHkg/3cfVvkHjPNciY4LO83yc= github.com/status-im/whisper v1.4.14/go.mod h1:WS6z39YJQ8WJa9s+DmTuEM/s2nVF6Iz3B1SZYw5cYf0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -487,6 +500,8 @@ golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -510,6 +525,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6 h1:FP8hkuE6yUEaJnK7O2eTuejKWwW+Rhfj80dQ2JcKxCU= golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -580,6 +597,8 @@ gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXa gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v9 v9.9.3 h1:Jp2VeUxCViEo9Gst3entIFWiSkZH29YU9iRgl6ePzl4= gopkg.in/go-playground/validator.v9 v9.9.3/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/go-playground/validator.v9 v9.29.0 h1:5ofssLNYgAA/inWn6rTZ4juWpRJUwEnXc1LG2IeXwgQ= +gopkg.in/go-playground/validator.v9 v9.29.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= diff --git a/logutils/logger.go b/logutils/logger.go new file mode 100644 index 000000000..9e01abbae --- /dev/null +++ b/logutils/logger.go @@ -0,0 +1,10 @@ +package logutils + +import ( + "github.com/ethereum/go-ethereum/log" +) + +// Logger returns the main logger instance used by status-go. +func Logger() log.Logger { + return log.Root() +} diff --git a/logutils/zap_adapter.go b/logutils/zap_adapter.go index 8633e653f..153759f10 100644 --- a/logutils/zap_adapter.go +++ b/logutils/zap_adapter.go @@ -3,9 +3,13 @@ package logutils import ( "fmt" "github.com/ethereum/go-ethereum/log" + "go.uber.org/zap" "go.uber.org/zap/zapcore" "math" + "sync" "time" + + protocol "github.com/status-im/status-protocol-go" ) type gethLoggerCore struct { @@ -113,3 +117,30 @@ func NewZapAdapter(logger log.Logger, enab zapcore.LevelEnabler) zapcore.Core { logger: logger, } } + +var registerOnce sync.Once + +// NewZapLoggerWithAdapter returns a logger forwarding all logs with level info and above. +func NewZapLoggerWithAdapter(logger log.Logger) (*zap.Logger, error) { + registerOnce.Do(func() { + if err := protocol.RegisterJSONHexEncoder(); err != nil { + panic(err) + } + }) + + cfg := zap.Config{ + Level: zap.NewAtomicLevelAt(zapcore.DebugLevel), + Development: false, + Sampling: nil, + Encoding: "json-hex", + EncoderConfig: zap.NewProductionEncoderConfig(), + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + } + adapter := zap.WrapCore( + func(zapcore.Core) zapcore.Core { + return NewZapAdapter(logger, cfg.Level) + }, + ) + return cfg.Build(adapter) +} diff --git a/logutils/zap_adapter_test.go b/logutils/zap_adapter_test.go index 0a5adbff0..77f84927c 100644 --- a/logutils/zap_adapter_test.go +++ b/logutils/zap_adapter_test.go @@ -11,7 +11,6 @@ import ( func TestNewZapAdapter(t *testing.T) { buf := bytes.NewBuffer(nil) - logger := log.New() handler := log.StreamHandler(buf, log.LogfmtFormat()) logger.SetHandler(handler) @@ -40,3 +39,19 @@ func TestNewZapAdapter(t *testing.T) { Info("some message with param") require.Contains(t, buf.String(), `lvl=info msg="some message with param" namespace=some-namespace site=SomeSite`) } + +func TestNewZapLoggerWithAdapter(t *testing.T) { + buf := bytes.NewBuffer(nil) + logger := log.New() + handler := log.StreamHandler(buf, log.LogfmtFormat()) + logger.SetHandler(handler) + + zapLogger, err := NewZapLoggerWithAdapter(logger) + require.NoError(t, err) + + buf.Reset() + zapLogger. + With(zap.Error(errors.New("some error"))). + Error("some message with error level") + require.Contains(t, buf.String(), `lvl=eror msg="some message with error level" error="some error`) +} diff --git a/messaging/chat/encryption_multi_device_test.go b/messaging/chat/encryption_multi_device_test.go deleted file mode 100644 index 34535dd35..000000000 --- a/messaging/chat/encryption_multi_device_test.go +++ /dev/null @@ -1,278 +0,0 @@ -package chat - -import ( - "crypto/ecdsa" - "fmt" - "github.com/ethereum/go-ethereum/crypto" - "github.com/stretchr/testify/suite" - "os" - "testing" - - "github.com/status-im/status-go/messaging/multidevice" - "github.com/status-im/status-go/messaging/sharedsecret" -) - -const ( - aliceUser = "alice" - bobUser = "bob" -) - -func TestEncryptionServiceMultiDeviceSuite(t *testing.T) { - suite.Run(t, new(EncryptionServiceMultiDeviceSuite)) -} - -type serviceAndKey struct { - services []*ProtocolService - key *ecdsa.PrivateKey -} - -type EncryptionServiceMultiDeviceSuite struct { - suite.Suite - services map[string]*serviceAndKey -} - -func setupUser(user string, s *EncryptionServiceMultiDeviceSuite, n int) error { - key, err := crypto.GenerateKey() - if err != nil { - return err - } - - s.services[user] = &serviceAndKey{ - key: key, - services: make([]*ProtocolService, n), - } - - for i := 0; i < n; i++ { - installationID := fmt.Sprintf("%s%d", user, i+1) - dbPath := fmt.Sprintf("/tmp/%s.db", installationID) - - os.Remove(dbPath) - - persistence, err := NewSQLLitePersistence(dbPath, "key") - if err != nil { - return err - } - // Initialize sharedsecret - multideviceConfig := &multidevice.Config{ - MaxInstallations: n - 1, - InstallationID: installationID, - ProtocolVersion: 1, - } - - sharedSecretService := sharedsecret.NewService(persistence.GetSharedSecretStorage()) - multideviceService := multidevice.New(multideviceConfig, persistence.GetMultideviceStorage()) - - protocol := NewProtocolService( - NewEncryptionService( - persistence, - DefaultEncryptionServiceConfig(installationID)), - sharedSecretService, - multideviceService, - func(s []*multidevice.Installation) {}, - func(s []*sharedsecret.Secret) {}, - ) - - s.services[user].services[i] = protocol - - } - - return nil -} - -func (s *EncryptionServiceMultiDeviceSuite) SetupTest() { - s.services = make(map[string]*serviceAndKey) - err := setupUser(aliceUser, s, 4) - s.Require().NoError(err) - - err = setupUser(bobUser, s, 4) - s.Require().NoError(err) -} - -func (s *EncryptionServiceMultiDeviceSuite) TestProcessPublicBundle() { - aliceKey := s.services[aliceUser].key - - alice2Bundle, err := s.services[aliceUser].services[1].GetBundle(aliceKey) - s.Require().NoError(err) - - alice2IdentityPK, err := ExtractIdentity(alice2Bundle) - s.Require().NoError(err) - - alice2Identity := fmt.Sprintf("0x%x", crypto.FromECDSAPub(alice2IdentityPK)) - - alice3Bundle, err := s.services[aliceUser].services[2].GetBundle(aliceKey) - s.Require().NoError(err) - - alice3IdentityPK, err := ExtractIdentity(alice2Bundle) - s.Require().NoError(err) - - alice3Identity := fmt.Sprintf("0x%x", crypto.FromECDSAPub(alice3IdentityPK)) - - // Add alice2 bundle - response, err := s.services[aliceUser].services[0].ProcessPublicBundle(aliceKey, alice2Bundle) - s.Require().NoError(err) - s.Require().Equal(multidevice.Installation{ - Identity: alice2Identity, - Version: 1, - ID: "alice2", - }, *response[0]) - - // Add alice3 bundle - response, err = s.services[aliceUser].services[0].ProcessPublicBundle(aliceKey, alice3Bundle) - s.Require().NoError(err) - s.Require().Equal(multidevice.Installation{ - Identity: alice3Identity, - Version: 1, - ID: "alice3", - }, *response[0]) - - // No installation is enabled - alice1MergedBundle1, err := s.services[aliceUser].services[0].GetBundle(aliceKey) - s.Require().NoError(err) - - s.Require().Equal(1, len(alice1MergedBundle1.GetSignedPreKeys())) - s.Require().NotNil(alice1MergedBundle1.GetSignedPreKeys()["alice1"]) - - // We enable the installations - err = s.services[aliceUser].services[0].EnableInstallation(&aliceKey.PublicKey, "alice2") - s.Require().NoError(err) - - err = s.services[aliceUser].services[0].EnableInstallation(&aliceKey.PublicKey, "alice3") - s.Require().NoError(err) - - alice1MergedBundle2, err := s.services[aliceUser].services[0].GetBundle(aliceKey) - s.Require().NoError(err) - - // We get back a bundle with all the installations - s.Require().Equal(3, len(alice1MergedBundle2.GetSignedPreKeys())) - s.Require().NotNil(alice1MergedBundle2.GetSignedPreKeys()["alice1"]) - s.Require().NotNil(alice1MergedBundle2.GetSignedPreKeys()["alice2"]) - s.Require().NotNil(alice1MergedBundle2.GetSignedPreKeys()["alice3"]) - - // We disable the installations - err = s.services[aliceUser].services[0].DisableInstallation(&aliceKey.PublicKey, "alice2") - s.Require().NoError(err) - - alice1MergedBundle3, err := s.services[aliceUser].services[0].GetBundle(aliceKey) - s.Require().NoError(err) - - // We get back a bundle with all the installations - s.Require().Equal(2, len(alice1MergedBundle3.GetSignedPreKeys())) - s.Require().NotNil(alice1MergedBundle3.GetSignedPreKeys()["alice1"]) - s.Require().NotNil(alice1MergedBundle3.GetSignedPreKeys()["alice3"]) -} - -func (s *EncryptionServiceMultiDeviceSuite) TestProcessPublicBundleOutOfOrder() { - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Alice1 creates a bundle - alice1Bundle, err := s.services[aliceUser].services[0].GetBundle(aliceKey) - s.Require().NoError(err) - - // Alice2 Receives the bundle - _, err = s.services[aliceUser].services[1].ProcessPublicBundle(aliceKey, alice1Bundle) - s.Require().NoError(err) - - // Alice2 Creates a Bundle - _, err = s.services[aliceUser].services[1].GetBundle(aliceKey) - s.Require().NoError(err) - - // We enable the installation - err = s.services[aliceUser].services[1].EnableInstallation(&aliceKey.PublicKey, "alice1") - s.Require().NoError(err) - - // It should contain both bundles - alice2MergedBundle1, err := s.services[aliceUser].services[1].GetBundle(aliceKey) - s.Require().NoError(err) - - s.Require().NotNil(alice2MergedBundle1.GetSignedPreKeys()["alice1"]) - s.Require().NotNil(alice2MergedBundle1.GetSignedPreKeys()["alice2"]) -} - -func pairDevices(s *serviceAndKey, target int) error { - device := s.services[target] - for i := 0; i < len(s.services); i++ { - b, err := s.services[i].GetBundle(s.key) - - if err != nil { - return err - } - - _, err = device.ProcessPublicBundle(s.key, b) - if err != nil { - return err - } - - err = device.EnableInstallation(&s.key.PublicKey, s.services[i].encryption.config.InstallationID) - if err != nil { - return nil - } - } - return nil -} - -func (s *EncryptionServiceMultiDeviceSuite) TestMaxDevices() { - err := pairDevices(s.services[aliceUser], 0) - s.Require().NoError(err) - alice1 := s.services[aliceUser].services[0] - bob1 := s.services[bobUser].services[0] - aliceKey := s.services[aliceUser].key - bobKey := s.services[bobUser].key - - // Check bundle is ok - // No installation is enabled - aliceBundle, err := alice1.GetBundle(aliceKey) - s.Require().NoError(err) - - // Check all installations are correctly working, and that the oldest device is not there - preKeys := aliceBundle.GetSignedPreKeys() - s.Require().Equal(3, len(preKeys)) - s.Require().NotNil(preKeys["alice1"]) - // alice2 being the oldest device is rotated out, as we reached the maximum - s.Require().Nil(preKeys["alice2"]) - s.Require().NotNil(preKeys["alice3"]) - s.Require().NotNil(preKeys["alice4"]) - - // We propagate this to bob - _, err = bob1.ProcessPublicBundle(bobKey, aliceBundle) - s.Require().NoError(err) - - // Bob sends a message to alice - msg, err := bob1.BuildDirectMessage(bobKey, &aliceKey.PublicKey, []byte("test")) - s.Require().NoError(err) - payload := msg.Message.GetDirectMessage() - s.Require().Equal(3, len(payload)) - s.Require().NotNil(payload["alice1"]) - s.Require().NotNil(payload["alice3"]) - s.Require().NotNil(payload["alice4"]) - - // We disable the last installation - err = s.services[aliceUser].services[0].DisableInstallation(&aliceKey.PublicKey, "alice4") - s.Require().NoError(err) - - // We check the bundle is updated - aliceBundle, err = alice1.GetBundle(aliceKey) - s.Require().NoError(err) - - // Check all installations are there - preKeys = aliceBundle.GetSignedPreKeys() - s.Require().Equal(3, len(preKeys)) - s.Require().NotNil(preKeys["alice1"]) - s.Require().NotNil(preKeys["alice2"]) - s.Require().NotNil(preKeys["alice3"]) - // alice4 is disabled at this point, alice2 is back in - s.Require().Nil(preKeys["alice4"]) - - // We propagate this to bob - _, err = bob1.ProcessPublicBundle(bobKey, aliceBundle) - s.Require().NoError(err) - - // Bob sends a message to alice - msg, err = bob1.BuildDirectMessage(bobKey, &aliceKey.PublicKey, []byte("test")) - s.Require().NoError(err) - payload = msg.Message.GetDirectMessage() - s.Require().Equal(3, len(payload)) - s.Require().NotNil(payload["alice1"]) - s.Require().NotNil(payload["alice2"]) - s.Require().NotNil(payload["alice3"]) -} diff --git a/messaging/chat/encryption_test.go b/messaging/chat/encryption_test.go deleted file mode 100644 index 8bd23bcd1..000000000 --- a/messaging/chat/encryption_test.go +++ /dev/null @@ -1,1018 +0,0 @@ -package chat - -import ( - "crypto/ecdsa" - "errors" - "fmt" - "io/ioutil" - "math/rand" - "os" - "reflect" - "sync" - "testing" - "time" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/status-im/status-go/messaging/chat/protobuf" - "github.com/status-im/status-go/messaging/multidevice" - "github.com/status-im/status-go/messaging/sharedsecret" - "github.com/stretchr/testify/suite" -) - -var cleartext = []byte("hello") -var aliceInstallationID = "1" -var bobInstallationID = "2" -var defaultMessageID = []byte("default") - -func TestEncryptionServiceTestSuite(t *testing.T) { - suite.Run(t, new(EncryptionServiceTestSuite)) -} - -type EncryptionServiceTestSuite struct { - suite.Suite - alice *ProtocolService - bob *ProtocolService - aliceDBPath string - bobDBPath string -} - -func (s *EncryptionServiceTestSuite) initDatabases(baseConfig *EncryptionServiceConfig) { - - aliceDBFile, err := ioutil.TempFile(os.TempDir(), "alice") - s.Require().NoError(err) - aliceDBPath := aliceDBFile.Name() - - bobDBFile, err := ioutil.TempFile(os.TempDir(), "bob") - s.Require().NoError(err) - bobDBPath := bobDBFile.Name() - - s.aliceDBPath = aliceDBPath - s.bobDBPath = bobDBPath - - if baseConfig == nil { - config := DefaultEncryptionServiceConfig(aliceInstallationID) - baseConfig = &config - } - - const ( - aliceDBKey = "alice" - bobDBKey = "bob" - ) - - aliceMultideviceConfig := &multidevice.Config{ - MaxInstallations: 3, - InstallationID: aliceInstallationID, - ProtocolVersion: 1, - } - - alicePersistence, err := NewSQLLitePersistence(aliceDBPath, aliceDBKey) - if err != nil { - panic(err) - } - - baseConfig.InstallationID = aliceInstallationID - aliceEncryptionService := NewEncryptionService(alicePersistence, *baseConfig) - - aliceSharedSecretService := sharedsecret.NewService(alicePersistence.GetSharedSecretStorage()) - aliceMultideviceService := multidevice.New(aliceMultideviceConfig, alicePersistence.GetMultideviceStorage()) - - s.alice = NewProtocolService( - aliceEncryptionService, - aliceSharedSecretService, - aliceMultideviceService, - func(s []*multidevice.Installation) {}, - func(s []*sharedsecret.Secret) {}, - ) - - bobPersistence, err := NewSQLLitePersistence(bobDBPath, bobDBKey) - if err != nil { - panic(err) - } - - bobMultideviceConfig := &multidevice.Config{ - MaxInstallations: 3, - InstallationID: bobInstallationID, - ProtocolVersion: 1, - } - - bobMultideviceService := multidevice.New(bobMultideviceConfig, bobPersistence.GetMultideviceStorage()) - - bobSharedSecretService := sharedsecret.NewService(bobPersistence.GetSharedSecretStorage()) - - baseConfig.InstallationID = bobInstallationID - bobEncryptionService := NewEncryptionService(bobPersistence, *baseConfig) - - s.bob = NewProtocolService( - bobEncryptionService, - bobSharedSecretService, - bobMultideviceService, - func(s []*multidevice.Installation) {}, - func(s []*sharedsecret.Secret) {}, - ) - -} - -func (s *EncryptionServiceTestSuite) SetupTest() { - s.initDatabases(nil) -} - -func (s *EncryptionServiceTestSuite) TearDownTest() { - os.Remove(s.aliceDBPath) - os.Remove(s.bobDBPath) -} - -func (s *EncryptionServiceTestSuite) TestGetBundle() { - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - aliceBundle1, err := s.alice.GetBundle(aliceKey) - s.Require().NoError(err) - s.NotNil(aliceBundle1, "It creates a bundle") - - aliceBundle2, err := s.alice.GetBundle(aliceKey) - s.Require().NoError(err) - s.Equal(aliceBundle1, aliceBundle2, "It returns the same bundle") -} - -// Alice sends Bob an encrypted message with DH using an ephemeral key -// and Bob's identity key. -// Bob is able to decrypt it. -// Alice does not re-use the symmetric key -func (s *EncryptionServiceTestSuite) TestEncryptPayloadNoBundle() { - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - response1, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext) - s.Require().NoError(err) - - encryptionResponse1 := response1.Message.GetDirectMessage() - - installationResponse1 := encryptionResponse1["none"] - // That's for any device - s.Require().NotNil(installationResponse1) - - cyphertext1 := installationResponse1.Payload - ephemeralKey1 := installationResponse1.GetDHHeader().GetKey() - s.NotNil(ephemeralKey1, "It generates an ephemeral key for DH exchange") - s.NotNil(cyphertext1, "It generates an encrypted payload") - s.NotEqual(cyphertext1, cleartext, "It encrypts the payload correctly") - - // On the receiver side, we should be able to decrypt using our private key and the ephemeral just sent - decryptedPayload1, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response1.Message, defaultMessageID) - s.Require().NoError(err) - s.Equal(cleartext, decryptedPayload1, "It correctly decrypts the payload using DH") - - // The next message will not be re-using the same key - response2, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext) - s.Require().NoError(err) - - encryptionResponse2 := response2.Message.GetDirectMessage() - - installationResponse2 := encryptionResponse2[aliceInstallationID] - - cyphertext2 := installationResponse2.GetPayload() - ephemeralKey2 := installationResponse2.GetDHHeader().GetKey() - s.NotEqual(cyphertext1, cyphertext2, "It does not re-use the symmetric key") - s.NotEqual(ephemeralKey1, ephemeralKey2, "It does not re-use the ephemeral key") - - decryptedPayload2, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response2.Message, defaultMessageID) - s.Require().NoError(err) - s.Equal(cleartext, decryptedPayload2, "It correctly decrypts the payload using DH") -} - -// Alice has Bob's bundle -// Alice sends Bob an encrypted message with X3DH and DR using an ephemeral key -// and Bob's bundle. -func (s *EncryptionServiceTestSuite) TestEncryptPayloadBundle() { - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Create a bundle - bobBundle, err := s.bob.GetBundle(bobKey) - s.Require().NoError(err) - - // We add bob bundle - _, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle) - s.Require().NoError(err) - - // We send a message using the bundle - response1, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext) - s.Require().NoError(err) - - encryptionResponse1 := response1.Message.GetDirectMessage() - - installationResponse1 := encryptionResponse1[bobInstallationID] - s.Require().NotNil(installationResponse1) - - cyphertext1 := installationResponse1.GetPayload() - x3dhHeader := installationResponse1.GetX3DHHeader() - drHeader := installationResponse1.GetDRHeader() - - s.NotNil(cyphertext1, "It generates an encrypted payload") - s.NotEqual(cyphertext1, cleartext, "It encrypts the payload correctly") - - // Check X3DH Header - bundleID := bobBundle.GetSignedPreKeys()[bobInstallationID].GetSignedPreKey() - - s.NotNil(x3dhHeader, "It adds an x3dh header") - s.NotNil(x3dhHeader.GetKey(), "It adds an ephemeral key") - s.Equal(x3dhHeader.GetId(), bundleID, "It sets the bundle id") - - // Check DR Header - s.NotNil(drHeader, "It adds a DR header") - s.NotNil(drHeader.GetKey(), "It adds a key to the DR header") - s.Equal(bundleID, drHeader.GetId(), "It adds the bundle id") - s.Equal(uint32(0), drHeader.GetN(), "It adds the correct message number") - s.Equal(uint32(0), drHeader.GetPn(), "It adds the correct length of the message chain") - - // Bob is able to decrypt it using the bundle - decryptedPayload1, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response1.Message, defaultMessageID) - s.Require().NoError(err) - s.Equal(cleartext, decryptedPayload1, "It correctly decrypts the payload using X3DH") -} - -// Alice has Bob's bundle -// Alice sends Bob 2 encrypted messages with X3DH and DR using an ephemeral key -// and Bob's bundle. -// Alice sends another message. This message should be using a DR -// and should include the initial x3dh message -// Bob receives only the last one, he should be able to decrypt it -// nolint: megacheck -func (s *EncryptionServiceTestSuite) TestConsequentMessagesBundle() { - cleartext1 := []byte("message 1") - cleartext2 := []byte("message 2") - - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Create a bundle - bobBundle, err := s.bob.GetBundle(bobKey) - s.Require().NoError(err) - - // We add bob bundle - _, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle) - s.Require().NoError(err) - - // We send a message using the bundle - _, err = s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext1) - s.Require().NoError(err) - - // We send another message using the bundle - response, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext2) - s.Require().NoError(err) - encryptionResponse := response.Message.GetDirectMessage() - - installationResponse := encryptionResponse[bobInstallationID] - s.Require().NotNil(installationResponse) - - cyphertext1 := installationResponse.GetPayload() - x3dhHeader := installationResponse.GetX3DHHeader() - drHeader := installationResponse.GetDRHeader() - - s.NotNil(cyphertext1, "It generates an encrypted payload") - s.NotEqual(cyphertext1, cleartext2, "It encrypts the payload correctly") - - // Check X3DH Header - bundleID := bobBundle.GetSignedPreKeys()[bobInstallationID].GetSignedPreKey() - - s.NotNil(x3dhHeader, "It adds an x3dh header") - s.NotNil(x3dhHeader.GetKey(), "It adds an ephemeral key") - s.Equal(x3dhHeader.GetId(), bundleID, "It sets the bundle id") - - // Check DR Header - s.NotNil(drHeader, "It adds a DR header") - s.NotNil(drHeader.GetKey(), "It adds a key to the DR header") - s.Equal(bundleID, drHeader.GetId(), "It adds the bundle id") - - s.Equal(uint32(1), drHeader.GetN(), "It adds the correct message number") - s.Equal(uint32(0), drHeader.GetPn(), "It adds the correct length of the message chain") - - // Bob is able to decrypt it using the bundle - decryptedPayload1, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response.Message, defaultMessageID) - s.Require().NoError(err) - - s.Equal(cleartext2, decryptedPayload1, "It correctly decrypts the payload using X3DH") -} - -// Alice has Bob's bundle -// Alice sends Bob an encrypted message with X3DH using an ephemeral key -// and Bob's bundle. -// Bob's receives the message -// Bob replies to the message -// Alice replies to the message - -func (s *EncryptionServiceTestSuite) TestConversation() { - cleartext1 := []byte("message 1") - cleartext2 := []byte("message 2") - - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Create a bundle - bobBundle, err := s.bob.GetBundle(bobKey) - s.Require().NoError(err) - - // Create a bundle - aliceBundle, err := s.alice.GetBundle(aliceKey) - s.Require().NoError(err) - - // We add bob bundle - _, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle) - s.Require().NoError(err) - - // We add alice bundle - _, err = s.bob.ProcessPublicBundle(bobKey, aliceBundle) - s.Require().NoError(err) - - // Alice sends a message - response, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext1) - s.Require().NoError(err) - - // Bob receives the message - _, err = s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response.Message, defaultMessageID) - s.Require().NoError(err) - - // Bob replies to the message - response, err = s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, cleartext1) - s.Require().NoError(err) - - // Alice receives the message - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, response.Message, defaultMessageID) - s.Require().NoError(err) - - // We send another message using the bundle - response, err = s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext2) - s.Require().NoError(err) - encryptionResponse := response.Message.GetDirectMessage() - - installationResponse := encryptionResponse[bobInstallationID] - s.Require().NotNil(installationResponse) - - cyphertext1 := installationResponse.GetPayload() - x3dhHeader := installationResponse.GetX3DHHeader() - drHeader := installationResponse.GetDRHeader() - - s.NotNil(cyphertext1, "It generates an encrypted payload") - s.NotEqual(cyphertext1, cleartext2, "It encrypts the payload correctly") - - // It does not send the x3dh bundle - s.Nil(x3dhHeader, "It does not add an x3dh header") - - // Check DR Header - bundleID := bobBundle.GetSignedPreKeys()[bobInstallationID].GetSignedPreKey() - - s.NotNil(drHeader, "It adds a DR header") - s.NotNil(drHeader.GetKey(), "It adds a key to the DR header") - s.Equal(bundleID, drHeader.GetId(), "It adds the bundle id") - - s.Equal(uint32(0), drHeader.GetN(), "It adds the correct message number") - s.Equal(uint32(1), drHeader.GetPn(), "It adds the correct length of the message chain") - - // Bob is able to decrypt it using the bundle - decryptedPayload1, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response.Message, defaultMessageID) - s.Require().NoError(err) - - s.Equal(cleartext2, decryptedPayload1, "It correctly decrypts the payload using X3DH") -} - -// Previous implementation allowed max maxSkip keys in the same receiving chain -// leading to a problem whereby dropped messages would accumulate and eventually -// we would not be able to decrypt any new message anymore. -// Here we are testing that maxSkip only applies to *consecutive* messages, not -// overall. -func (s *EncryptionServiceTestSuite) TestMaxSkipKeys() { - bobText := []byte("text") - - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Create a bundle - bobBundle, err := s.bob.GetBundle(bobKey) - s.Require().NoError(err) - - // We add bob bundle - _, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle) - s.Require().NoError(err) - - // Create a bundle - aliceBundle, err := s.alice.GetBundle(aliceKey) - s.Require().NoError(err) - - // We add alice bundle - _, err = s.bob.ProcessPublicBundle(bobKey, aliceBundle) - s.Require().NoError(err) - - // Bob sends a message - - for i := 0; i < s.alice.encryption.config.MaxSkip; i++ { - _, err = s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText) - s.Require().NoError(err) - } - - // Bob sends a message - bobMessage1, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText) - s.Require().NoError(err) - - // Alice receives the message - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, bobMessage1.Message, defaultMessageID) - s.Require().NoError(err) - - // Bob sends a message - _, err = s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText) - s.Require().NoError(err) - - // Bob sends a message - bobMessage2, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText) - s.Require().NoError(err) - - // Alice receives the message, we should have maxSkip + 1 keys in the db, but - // we should not throw an error - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, bobMessage2.Message, defaultMessageID) - s.Require().NoError(err) -} - -// Test that an error is thrown if max skip is reached -func (s *EncryptionServiceTestSuite) TestMaxSkipKeysError() { - bobText := []byte("text") - - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Create a bundle - bobBundle, err := s.bob.GetBundle(bobKey) - s.Require().NoError(err) - - // We add bob bundle - _, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle) - s.Require().NoError(err) - - // Create a bundle - aliceBundle, err := s.alice.GetBundle(aliceKey) - s.Require().NoError(err) - - // We add alice bundle - _, err = s.bob.ProcessPublicBundle(bobKey, aliceBundle) - s.Require().NoError(err) - - // Bob sends a message - - for i := 0; i < s.alice.encryption.config.MaxSkip+1; i++ { - _, err = s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText) - s.Require().NoError(err) - } - - // Bob sends a message - bobMessage1, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText) - s.Require().NoError(err) - - // Alice receives the message - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, bobMessage1.Message, defaultMessageID) - s.Require().Equal(errors.New("can't skip current chain message keys: too many messages"), err) -} - -func (s *EncryptionServiceTestSuite) TestMaxMessageKeysPerSession() { - config := DefaultEncryptionServiceConfig("none") - // Set MaxKeep and MaxSkip to an high value so it does not interfere - config.MaxKeep = 100000 - config.MaxSkip = 100000 - - s.initDatabases(&config) - - bobText := []byte("text") - - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Create a bundle - bobBundle, err := s.bob.GetBundle(bobKey) - s.Require().NoError(err) - - // We add bob bundle - _, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle) - s.Require().NoError(err) - - // Create a bundle - aliceBundle, err := s.alice.GetBundle(aliceKey) - s.Require().NoError(err) - - // We add alice bundle - _, err = s.bob.ProcessPublicBundle(bobKey, aliceBundle) - s.Require().NoError(err) - - // We create just enough messages so that the first key should be deleted - - nMessages := s.alice.encryption.config.MaxMessageKeysPerSession - messages := make([]*protobuf.ProtocolMessage, nMessages) - for i := 0; i < nMessages; i++ { - m, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText) - s.Require().NoError(err) - - messages[i] = m.Message - } - - // Another message to trigger the deletion - m, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText) - s.Require().NoError(err) - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, m.Message, defaultMessageID) - s.Require().NoError(err) - - // We decrypt the first message, and it should fail - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, messages[0], defaultMessageID) - s.Require().Equal(errors.New("can't skip current chain message keys: bad until: probably an out-of-order message that was deleted"), err) - - // We decrypt the second message, and it should be decrypted - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, messages[1], defaultMessageID) - s.Require().NoError(err) -} - -func (s *EncryptionServiceTestSuite) TestMaxKeep() { - config := DefaultEncryptionServiceConfig("none") - // Set MaxMessageKeysPerSession to an high value so it does not interfere - config.MaxMessageKeysPerSession = 100000 - - s.initDatabases(&config) - - bobText := []byte("text") - - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Create a bundle - bobBundle, err := s.bob.GetBundle(bobKey) - s.Require().NoError(err) - - // We add bob bundle - _, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle) - s.Require().NoError(err) - - // Create a bundle - aliceBundle, err := s.alice.GetBundle(aliceKey) - s.Require().NoError(err) - - // We add alice bundle - _, err = s.bob.ProcessPublicBundle(bobKey, aliceBundle) - s.Require().NoError(err) - - // We decrypt all messages but 1 & 2 - messages := make([]*protobuf.ProtocolMessage, s.alice.encryption.config.MaxKeep) - for i := 0; i < s.alice.encryption.config.MaxKeep; i++ { - m, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText) - messages[i] = m.Message - s.Require().NoError(err) - - if i != 0 && i != 1 { - messageID := []byte(fmt.Sprintf("%d", i)) - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, m.Message, messageID) - s.Require().NoError(err) - err = s.alice.ConfirmMessagesProcessed([][]byte{messageID}) - s.Require().NoError(err) - } - - } - - // We decrypt the first message, and it should fail, as it should have been removed - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, messages[0], defaultMessageID) - s.Require().Equal(errors.New("can't skip current chain message keys: bad until: probably an out-of-order message that was deleted"), err) - - // We decrypt the second message, and it should be decrypted - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, messages[1], defaultMessageID) - s.Require().NoError(err) -} - -// Alice has Bob's bundle -// Bob has Alice's bundle -// Bob sends a message to alice -// Alice sends a message to Bob -// Bob receives alice message -// Alice receives Bob message -// Bob sends another message to alice and viceversa - -func (s *EncryptionServiceTestSuite) TestConcurrentBundles() { - bobText1 := []byte("bob text 1") - bobText2 := []byte("bob text 2") - aliceText1 := []byte("alice text 1") - aliceText2 := []byte("alice text 2") - - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Create a bundle - bobBundle, err := s.bob.GetBundle(bobKey) - s.Require().NoError(err) - - // We add bob bundle - _, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle) - s.Require().NoError(err) - - // Create a bundle - aliceBundle, err := s.alice.GetBundle(aliceKey) - s.Require().NoError(err) - - // We add alice bundle - _, err = s.bob.ProcessPublicBundle(bobKey, aliceBundle) - s.Require().NoError(err) - - // Alice sends a message - aliceMessage1, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, aliceText1) - s.Require().NoError(err) - - // Bob sends a message - bobMessage1, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText1) - s.Require().NoError(err) - - // Bob receives the message - _, err = s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, aliceMessage1.Message, defaultMessageID) - s.Require().NoError(err) - - // Alice receives the message - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, bobMessage1.Message, defaultMessageID) - s.Require().NoError(err) - - // Bob replies to the message - bobMessage2, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText2) - s.Require().NoError(err) - - // Alice sends a message - aliceMessage2, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, aliceText2) - s.Require().NoError(err) - - // Alice receives the message - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, bobMessage2.Message, defaultMessageID) - s.Require().NoError(err) - - // Bob receives the message - _, err = s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, aliceMessage2.Message, defaultMessageID) - s.Require().NoError(err) -} - -func publisher( - e *ProtocolService, - privateKey *ecdsa.PrivateKey, - publicKey *ecdsa.PublicKey, - errChan chan error, - output chan *protobuf.ProtocolMessage, -) { - var wg sync.WaitGroup - - for i := 0; i < 200; i++ { - - // Simulate 5% of the messages dropped - if rand.Intn(100) <= 95 { - wg.Add(1) - // Simulate out of order messages - go func() { - defer wg.Done() - time.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond) - response, err := e.BuildDirectMessage(privateKey, publicKey, cleartext) - if err != nil { - errChan <- err - return - } - - output <- response.Message - }() - } - } - wg.Wait() - close(output) - close(errChan) -} - -func receiver( - s *ProtocolService, - privateKey *ecdsa.PrivateKey, - publicKey *ecdsa.PublicKey, - errChan chan error, - input chan *protobuf.ProtocolMessage, -) { - i := 0 - - for payload := range input { - actualCleartext, err := s.HandleMessage(privateKey, publicKey, payload, defaultMessageID) - if err != nil { - errChan <- err - return - } - if !reflect.DeepEqual(actualCleartext, cleartext) { - errChan <- errors.New("Decrypted value does not match") - return - } - i++ - } - close(errChan) -} - -func (s *EncryptionServiceTestSuite) TestRandomised() { - - seed := time.Now().UTC().UnixNano() - rand.Seed(seed) - - // Print so that if it fails it can be replicated - fmt.Printf("Starting test with seed: %x\n", seed) - - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Create a bundle - bobBundle, err := s.bob.GetBundle(bobKey) - s.Require().NoError(err) - - // We add bob bundle - _, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle) - s.Require().NoError(err) - - // Create a bundle - aliceBundle, err := s.alice.GetBundle(aliceKey) - s.Require().NoError(err) - - // We add alice bundle - _, err = s.bob.ProcessPublicBundle(bobKey, aliceBundle) - s.Require().NoError(err) - - aliceChan := make(chan *protobuf.ProtocolMessage, 100) - bobChan := make(chan *protobuf.ProtocolMessage, 100) - - alicePublisherErrChan := make(chan error, 1) - bobPublisherErrChan := make(chan error, 1) - - aliceReceiverErrChan := make(chan error, 1) - bobReceiverErrChan := make(chan error, 1) - - // Set up alice publishe - go publisher(s.alice, aliceKey, &bobKey.PublicKey, alicePublisherErrChan, bobChan) - // Set up bob publisher - go publisher(s.bob, bobKey, &aliceKey.PublicKey, bobPublisherErrChan, aliceChan) - - // Set up bob receiver - go receiver(s.bob, bobKey, &aliceKey.PublicKey, bobReceiverErrChan, bobChan) - - // Set up alice receiver - go receiver(s.alice, aliceKey, &bobKey.PublicKey, aliceReceiverErrChan, aliceChan) - - aliceErr := <-alicePublisherErrChan - s.Require().NoError(aliceErr) - - bobErr := <-bobPublisherErrChan - s.Require().NoError(bobErr) - - aliceErr = <-aliceReceiverErrChan - s.Require().NoError(aliceErr) - - bobErr = <-bobReceiverErrChan - s.Require().NoError(bobErr) -} - -// Edge cases - -// The bundle is lost -func (s *EncryptionServiceTestSuite) TestBundleNotExisting() { - aliceText := []byte("alice text") - - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Create a bundle without saving it - bobBundleContainer, err := NewBundleContainer(bobKey, bobInstallationID) - s.Require().NoError(err) - - err = SignBundle(bobKey, bobBundleContainer) - s.Require().NoError(err) - - bobBundle := bobBundleContainer.GetBundle() - - // We add bob bundle - _, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle) - s.Require().NoError(err) - - // Alice sends a message - aliceMessage, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, aliceText) - s.Require().NoError(err) - - // Bob receives the message, and returns a bundlenotfound error - _, err = s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, aliceMessage.Message, defaultMessageID) - s.Require().Error(err) - s.Equal(ErrSessionNotFound, err) -} - -// Device is not included in the bundle -func (s *EncryptionServiceTestSuite) TestDeviceNotIncluded() { - bobDevice2InstallationID := "bob2" - - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Create a bundle without saving it - bobBundleContainer, err := NewBundleContainer(bobKey, bobDevice2InstallationID) - s.Require().NoError(err) - - err = SignBundle(bobKey, bobBundleContainer) - s.Require().NoError(err) - - bobBundle := bobBundleContainer.GetBundle() - - // We add bob bundle - _, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle) - s.Require().NoError(err) - - // Alice sends a message - aliceMessage, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, []byte("does not matter")) - s.Require().NoError(err) - - // Bob receives the message, and returns a bundlenotfound error - _, err = s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, aliceMessage.Message, defaultMessageID) - s.Require().Error(err) - s.Equal(ErrDeviceNotFound, err) -} - -// A new bundle has been received -func (s *EncryptionServiceTestSuite) TestRefreshedBundle() { - - config := DefaultEncryptionServiceConfig("none") - // Set up refresh interval to "always" - config.BundleRefreshInterval = 1000 - - s.initDatabases(&config) - - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Create bundles - bobBundle1, err := s.bob.GetBundle(bobKey) - s.Require().NoError(err) - s.Require().Equal(uint32(1), bobBundle1.GetSignedPreKeys()[bobInstallationID].GetVersion()) - - // Sleep the required time so that bundle is refreshed - time.Sleep(time.Duration(config.BundleRefreshInterval) * time.Millisecond) - - // Create bundles - bobBundle2, err := s.bob.GetBundle(bobKey) - s.Require().NoError(err) - s.Require().Equal(uint32(2), bobBundle2.GetSignedPreKeys()[bobInstallationID].GetVersion()) - - // We add the first bob bundle - _, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle1) - s.Require().NoError(err) - - // Alice sends a message - response1, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, []byte("anything")) - s.Require().NoError(err) - encryptionResponse1 := response1.Message.GetDirectMessage() - - installationResponse1 := encryptionResponse1[bobInstallationID] - s.Require().NotNil(installationResponse1) - - // This message is using bobBundle1 - - x3dhHeader1 := installationResponse1.GetX3DHHeader() - s.NotNil(x3dhHeader1) - s.Equal(bobBundle1.GetSignedPreKeys()[bobInstallationID].GetSignedPreKey(), x3dhHeader1.GetId()) - - // Bob decrypts the message - _, err = s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response1.Message, defaultMessageID) - s.Require().NoError(err) - - // We add the second bob bundle - _, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle2) - s.Require().NoError(err) - - // Alice sends a message - response2, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, []byte("anything")) - s.Require().NoError(err) - encryptionResponse2 := response2.Message.GetDirectMessage() - - installationResponse2 := encryptionResponse2[bobInstallationID] - s.Require().NotNil(installationResponse2) - - // This message is using bobBundle2 - - x3dhHeader2 := installationResponse2.GetX3DHHeader() - s.NotNil(x3dhHeader2) - s.Equal(bobBundle2.GetSignedPreKeys()[bobInstallationID].GetSignedPreKey(), x3dhHeader2.GetId()) - - // Bob decrypts the message - _, err = s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response2.Message, defaultMessageID) - s.Require().NoError(err) -} - -func (s *EncryptionServiceTestSuite) TestMessageConfirmation() { - bobText1 := []byte("bob text 1") - - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Create a bundle - bobBundle, err := s.bob.GetBundle(bobKey) - s.Require().NoError(err) - - // We add bob bundle - _, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle) - s.Require().NoError(err) - - // Create a bundle - aliceBundle, err := s.alice.GetBundle(aliceKey) - s.Require().NoError(err) - - // We add alice bundle - _, err = s.bob.ProcessPublicBundle(bobKey, aliceBundle) - s.Require().NoError(err) - - // Bob sends a message - bobMessage1, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText1) - s.Require().NoError(err) - bobMessage1ID := []byte("bob-message-1-id") - - // Alice receives the message once - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, bobMessage1.Message, bobMessage1ID) - s.Require().NoError(err) - - // Alice receives the message twice - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, bobMessage1.Message, bobMessage1ID) - s.Require().NoError(err) - - // Alice confirms the message - err = s.alice.ConfirmMessagesProcessed([][]byte{bobMessage1ID}) - s.Require().NoError(err) - - // Alice decrypts it again, it should fail - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, bobMessage1.Message, bobMessage1ID) - s.Require().Equal(errors.New("can't skip current chain message keys: bad until: probably an out-of-order message that was deleted"), err) - - // Bob sends a message - bobMessage2, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText1) - s.Require().NoError(err) - bobMessage2ID := []byte("bob-message-2-id") - - // Bob sends a message - bobMessage3, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText1) - s.Require().NoError(err) - bobMessage3ID := []byte("bob-message-3-id") - - // Alice receives message 3 once - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, bobMessage3.Message, bobMessage3ID) - s.Require().NoError(err) - - // Alice receives message 3 twice - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, bobMessage3.Message, bobMessage3ID) - s.Require().NoError(err) - - // Alice receives message 2 once - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, bobMessage2.Message, bobMessage2ID) - s.Require().NoError(err) - - // Alice receives message 2 twice - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, bobMessage2.Message, bobMessage2ID) - s.Require().NoError(err) - - // Alice confirms the messages - err = s.alice.ConfirmMessagesProcessed([][]byte{bobMessage2ID, bobMessage3ID}) - s.Require().NoError(err) - - // Alice decrypts it again, it should fail - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, bobMessage3.Message, bobMessage3ID) - s.Require().Equal(errors.New("can't skip current chain message keys: bad until: probably an out-of-order message that was deleted"), err) - - // Alice decrypts it again, it should fail - _, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, bobMessage2.Message, bobMessage2ID) - s.Require().Equal(errors.New("can't skip current chain message keys: bad until: probably an out-of-order message that was deleted"), err) -} diff --git a/messaging/chat/persistence.go b/messaging/chat/persistence.go deleted file mode 100644 index ee855f357..000000000 --- a/messaging/chat/persistence.go +++ /dev/null @@ -1,53 +0,0 @@ -package chat - -import ( - "crypto/ecdsa" - - dr "github.com/status-im/doubleratchet" - "github.com/status-im/status-go/messaging/chat/protobuf" - "github.com/status-im/status-go/messaging/multidevice" -) - -// RatchetInfo holds the current ratchet state -type RatchetInfo struct { - ID []byte - Sk []byte - PrivateKey []byte - PublicKey []byte - Identity []byte - BundleID []byte - EphemeralKey []byte - InstallationID string -} - -// Persistence defines the interface for a storage service -type Persistence interface { - // GetKeysStorage returns the associated double ratchet KeysStorage object. - GetKeysStorage() dr.KeysStorage - // GetSessionStorage returns the associated double ratchet SessionStorage object. - GetSessionStorage() dr.SessionStorage - - // GetPublicBundle retrieves an existing Bundle for the specified public key & installations - GetPublicBundle(*ecdsa.PublicKey, []*multidevice.Installation) (*protobuf.Bundle, error) - // AddPublicBundle persists a specified Bundle - AddPublicBundle(*protobuf.Bundle) error - - // GetAnyPrivateBundle retrieves any bundle for our identity & installations - GetAnyPrivateBundle([]byte, []*multidevice.Installation) (*protobuf.BundleContainer, error) - // GetPrivateKeyBundle retrieves a BundleContainer with the specified signed prekey. - GetPrivateKeyBundle([]byte) ([]byte, error) - // AddPrivateBundle persists a BundleContainer. - AddPrivateBundle(*protobuf.BundleContainer) error - // MarkBundleExpired marks a private bundle as expired, not to be used for encryption anymore. - MarkBundleExpired([]byte) error - - // AddRatchetInfo persists the specified ratchet info - AddRatchetInfo([]byte, []byte, []byte, []byte, string) error - // GetRatchetInfo retrieves the existing RatchetInfo for a specified bundle ID and interlocutor public key. - GetRatchetInfo([]byte, []byte, string) (*RatchetInfo, error) - // GetAnyRatchetInfo retrieves any existing RatchetInfo for a specified interlocutor public key. - GetAnyRatchetInfo([]byte, string) (*RatchetInfo, error) - // RatchetInfoConfirmed clears the ephemeral key in the RatchetInfo - // associated with the specified bundle ID and interlocutor identity public key. - RatchetInfoConfirmed([]byte, []byte, string) error -} diff --git a/messaging/chat/protocol.go b/messaging/chat/protocol.go deleted file mode 100644 index 7dc1e59ef..000000000 --- a/messaging/chat/protocol.go +++ /dev/null @@ -1,386 +0,0 @@ -package chat - -import ( - "crypto/ecdsa" - "errors" - "fmt" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - - "github.com/status-im/status-go/messaging/chat/protobuf" - "github.com/status-im/status-go/messaging/multidevice" - "github.com/status-im/status-go/messaging/sharedsecret" -) - -const ProtocolVersion = 1 -const sharedSecretNegotiationVersion = 1 -const partitionedTopicMinVersion = 1 -const defaultMinVersion = 0 - -type PartitionTopic int - -const ( - PartitionTopicNoSupport PartitionTopic = iota - PartitionTopicV1 -) - -type ProtocolService struct { - log log.Logger - encryption *EncryptionService - secret *sharedsecret.Service - multidevice *multidevice.Service - addedBundlesHandler func([]*multidevice.Installation) - onNewSharedSecretHandler func([]*sharedsecret.Secret) - Enabled bool -} - -var ( - ErrNotProtocolMessage = errors.New("not a protocol message") - ErrNoPayload = errors.New("no payload") -) - -// NewProtocolService creates a new ProtocolService instance -func NewProtocolService(encryption *EncryptionService, secret *sharedsecret.Service, multidevice *multidevice.Service, addedBundlesHandler func([]*multidevice.Installation), onNewSharedSecretHandler func([]*sharedsecret.Secret)) *ProtocolService { - return &ProtocolService{ - log: log.New("package", "status-go/services/sshext.chat"), - encryption: encryption, - secret: secret, - multidevice: multidevice, - addedBundlesHandler: addedBundlesHandler, - onNewSharedSecretHandler: onNewSharedSecretHandler, - } -} - -func (p *ProtocolService) addBundle(myIdentityKey *ecdsa.PrivateKey, msg *protobuf.ProtocolMessage, sendSingle bool) (*protobuf.ProtocolMessage, error) { - - // Get a bundle - installations, err := p.multidevice.GetOurActiveInstallations(&myIdentityKey.PublicKey) - if err != nil { - return nil, err - } - - bundle, err := p.encryption.CreateBundle(myIdentityKey, installations) - if err != nil { - p.log.Error("encryption-service", "error creating bundle", err) - return nil, err - } - - if sendSingle { - // DEPRECATED: This is only for backward compatibility, remove once not - // an issue anymore - msg.Bundle = bundle - } else { - msg.Bundles = []*protobuf.Bundle{bundle} - } - - return msg, nil -} - -// BuildPublicMessage marshals a public chat message given the user identity private key and a payload -func (p *ProtocolService) BuildPublicMessage(myIdentityKey *ecdsa.PrivateKey, payload []byte) (*protobuf.ProtocolMessage, error) { - // Build message not encrypted - protocolMessage := &protobuf.ProtocolMessage{ - InstallationId: p.encryption.config.InstallationID, - PublicMessage: payload, - } - - return p.addBundle(myIdentityKey, protocolMessage, false) -} - -type ProtocolMessageSpec struct { - Message *protobuf.ProtocolMessage - // Installations is the targeted devices - Installations []*multidevice.Installation - // SharedSecret is a shared secret established among the installations - SharedSecret []byte -} - -func (p *ProtocolMessageSpec) MinVersion() uint32 { - - if len(p.Installations) == 0 { - return defaultMinVersion - } - - version := p.Installations[0].Version - - for _, installation := range p.Installations[1:] { - if installation.Version < version { - version = installation.Version - } - } - return version -} - -func (p *ProtocolMessageSpec) PartitionedTopic() PartitionTopic { - if p.MinVersion() >= partitionedTopicMinVersion { - return PartitionTopicV1 - } - return PartitionTopicNoSupport -} - -// BuildDirectMessage returns a 1:1 chat message and optionally a negotiated topic given the user identity private key, the recipient's public key, and a payload -func (p *ProtocolService) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, payload []byte) (*ProtocolMessageSpec, error) { - activeInstallations, err := p.multidevice.GetActiveInstallations(publicKey) - if err != nil { - return nil, err - } - - // Encrypt payload - encryptionResponse, installations, err := p.encryption.EncryptPayload(publicKey, myIdentityKey, activeInstallations, payload) - if err != nil { - p.log.Error("encryption-service", "error encrypting payload", err) - return nil, err - } - - // Build message - protocolMessage := &protobuf.ProtocolMessage{ - InstallationId: p.encryption.config.InstallationID, - DirectMessage: encryptionResponse, - } - - msg, err := p.addBundle(myIdentityKey, protocolMessage, true) - if err != nil { - return nil, err - } - - // Check who we are sending the message to, and see if we have a shared secret - // across devices - var installationIDs []string - var sharedSecret *sharedsecret.Secret - var agreed bool - for installationID := range protocolMessage.GetDirectMessage() { - if installationID != noInstallationID { - installationIDs = append(installationIDs, installationID) - } - } - - sharedSecret, agreed, err = p.secret.Send(myIdentityKey, p.encryption.config.InstallationID, publicKey, installationIDs) - if err != nil { - return nil, err - } - - // Call handler - if sharedSecret != nil { - p.onNewSharedSecretHandler([]*sharedsecret.Secret{sharedSecret}) - } - response := &ProtocolMessageSpec{ - Message: msg, - Installations: installations, - } - - if agreed { - response.SharedSecret = sharedSecret.Key - } - return response, nil -} - -// BuildDHMessage builds a message with DH encryption so that it can be decrypted by any other device. -func (p *ProtocolService) BuildDHMessage(myIdentityKey *ecdsa.PrivateKey, destination *ecdsa.PublicKey, payload []byte) (*ProtocolMessageSpec, error) { - // Encrypt payload - encryptionResponse, err := p.encryption.EncryptPayloadWithDH(destination, payload) - if err != nil { - p.log.Error("encryption-service", "error encrypting payload", err) - return nil, err - } - - // Build message - protocolMessage := &protobuf.ProtocolMessage{ - InstallationId: p.encryption.config.InstallationID, - DirectMessage: encryptionResponse, - } - - msg, err := p.addBundle(myIdentityKey, protocolMessage, true) - if err != nil { - return nil, err - } - - return &ProtocolMessageSpec{Message: msg}, nil -} - -// ProcessPublicBundle processes a received X3DH bundle. -func (p *ProtocolService) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *protobuf.Bundle) ([]*multidevice.Installation, error) { - p.log.Debug("Processing bundle", "bundle", bundle) - - if err := p.encryption.ProcessPublicBundle(myIdentityKey, bundle); err != nil { - return nil, err - } - - installations, fromOurs, err := p.recoverInstallationsFromBundle(myIdentityKey, bundle) - if err != nil { - return nil, err - } - - // TODO(adam): why do we add installations using identity obtained from GetIdentity() - // instead of the output of crypto.CompressPubkey()? I tried the second option - // and the unit tests TestTopic and TestMaxDevices fail. - return p.multidevice.AddInstallations(bundle.GetIdentity(), bundle.GetTimestamp(), installations, fromOurs) -} - -// recoverInstallationsFromBundle extracts installations from the bundle. -// It returns extracted installations and true if the installations -// are ours, i.e. the bundle was created by our identity key. -func (p *ProtocolService) recoverInstallationsFromBundle(myIdentityKey *ecdsa.PrivateKey, bundle *protobuf.Bundle) ([]*multidevice.Installation, bool, error) { - var installations []*multidevice.Installation - - theirIdentity, err := ExtractIdentity(bundle) - if err != nil { - return nil, false, err - } - - myIdentityStr := fmt.Sprintf("0x%x", crypto.FromECDSAPub(&myIdentityKey.PublicKey)) - theirIdentityStr := fmt.Sprintf("0x%x", crypto.FromECDSAPub(theirIdentity)) - // Any device from other peers will be considered enabled, ours needs to - // be explicitly enabled - fromOurIdentity := theirIdentityStr != myIdentityStr - signedPreKeys := bundle.GetSignedPreKeys() - - for installationID, signedPreKey := range signedPreKeys { - if installationID != p.multidevice.InstallationID() { - installations = append(installations, &multidevice.Installation{ - Identity: theirIdentityStr, - ID: installationID, - Version: signedPreKey.GetProtocolVersion(), - }) - } - } - - return installations, fromOurIdentity, nil -} - -// GetBundle retrieves or creates a X3DH bundle, given a private identity key. -func (p *ProtocolService) GetBundle(myIdentityKey *ecdsa.PrivateKey) (*protobuf.Bundle, error) { - installations, err := p.multidevice.GetOurActiveInstallations(&myIdentityKey.PublicKey) - if err != nil { - return nil, err - } - - return p.encryption.CreateBundle(myIdentityKey, installations) -} - -// EnableInstallation enables an installation for multi-device sync. -func (p *ProtocolService) EnableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error { - return p.multidevice.EnableInstallation(myIdentityKey, installationID) -} - -// DisableInstallation disables an installation for multi-device sync. -func (p *ProtocolService) DisableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error { - return p.multidevice.DisableInstallation(myIdentityKey, installationID) -} - -// GetOurInstallations returns all the installations available given an identity -func (p *ProtocolService) GetOurInstallations(myIdentityKey *ecdsa.PublicKey) ([]*multidevice.Installation, error) { - return p.multidevice.GetOurInstallations(myIdentityKey) -} - -// SetInstallationMetadata sets the metadata for our own installation -func (p *ProtocolService) SetInstallationMetadata(myIdentityKey *ecdsa.PublicKey, installationID string, data *multidevice.InstallationMetadata) error { - return p.multidevice.SetInstallationMetadata(myIdentityKey, installationID, data) -} - -// GetPublicBundle retrieves a public bundle given an identity -func (p *ProtocolService) GetPublicBundle(theirIdentityKey *ecdsa.PublicKey) (*protobuf.Bundle, error) { - installations, err := p.multidevice.GetActiveInstallations(theirIdentityKey) - if err != nil { - return nil, err - } - return p.encryption.GetPublicBundle(theirIdentityKey, installations) -} - -// ConfirmMessagesProcessed confirms and deletes message keys for the given messages -func (p *ProtocolService) ConfirmMessagesProcessed(messageIDs [][]byte) error { - return p.encryption.ConfirmMessagesProcessed(messageIDs) -} - -func (p *ProtocolService) GetSharedSecretService() *sharedsecret.Service { - return p.secret -} - -// HandleMessage unmarshals a message and processes it, decrypting it if it is a 1:1 message. -func (p *ProtocolService) HandleMessage(myIdentityKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, protocolMessage *protobuf.ProtocolMessage, messageID []byte) ([]byte, error) { - p.log.Debug("Received message from", "public-key", theirPublicKey) - if p.encryption == nil { - return nil, errors.New("encryption service not initialized") - } - - // Process bundle, deprecated, here for backward compatibility - if bundle := protocolMessage.GetBundle(); bundle != nil { - // Should we stop processing if the bundle cannot be verified? - addedBundles, err := p.ProcessPublicBundle(myIdentityKey, bundle) - if err != nil { - return nil, err - } - - p.addedBundlesHandler(addedBundles) - } - - // Process bundles - for _, bundle := range protocolMessage.GetBundles() { - // Should we stop processing if the bundle cannot be verified? - addedBundles, err := p.ProcessPublicBundle(myIdentityKey, bundle) - if err != nil { - return nil, err - } - - p.addedBundlesHandler(addedBundles) - } - - // Check if it's a public message - if publicMessage := protocolMessage.GetPublicMessage(); publicMessage != nil { - p.log.Debug("Public message, nothing to do") - // Nothing to do, as already in cleartext - return publicMessage, nil - } - - // Decrypt message - if directMessage := protocolMessage.GetDirectMessage(); directMessage != nil { - p.log.Debug("Processing direct message") - message, err := p.encryption.DecryptPayload(myIdentityKey, theirPublicKey, protocolMessage.GetInstallationId(), directMessage, messageID) - if err != nil { - return nil, err - } - - // Handle protocol negotiation for compatible clients - bundles := append(protocolMessage.GetBundles(), protocolMessage.GetBundle()) - version := getProtocolVersion(bundles, protocolMessage.GetInstallationId()) - p.log.Debug("Message version is", "version", version) - if version >= sharedSecretNegotiationVersion { - p.log.Debug("Negotiating shared secret") - sharedSecret, err := p.secret.Receive(myIdentityKey, theirPublicKey, protocolMessage.GetInstallationId()) - if err != nil { - return nil, err - } - - p.onNewSharedSecretHandler([]*sharedsecret.Secret{sharedSecret}) - - } - return message, nil - } - - // Return error - return nil, ErrNoPayload -} - -func getProtocolVersion(bundles []*protobuf.Bundle, installationID string) uint32 { - if installationID == "" { - return defaultMinVersion - } - - for _, bundle := range bundles { - if bundle != nil { - signedPreKeys := bundle.GetSignedPreKeys() - if signedPreKeys == nil { - continue - } - - signedPreKey := signedPreKeys[installationID] - if signedPreKey == nil { - return defaultMinVersion - } - - return signedPreKey.GetProtocolVersion() - } - } - - return defaultMinVersion -} diff --git a/messaging/chat/protocol_test.go b/messaging/chat/protocol_test.go deleted file mode 100644 index d303d5331..000000000 --- a/messaging/chat/protocol_test.go +++ /dev/null @@ -1,171 +0,0 @@ -package chat - -import ( - "os" - "testing" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/status-im/status-go/messaging/multidevice" - "github.com/status-im/status-go/messaging/sharedsecret" - "github.com/stretchr/testify/suite" -) - -func TestProtocolServiceTestSuite(t *testing.T) { - suite.Run(t, new(ProtocolServiceTestSuite)) -} - -type ProtocolServiceTestSuite struct { - suite.Suite - alice *ProtocolService - bob *ProtocolService -} - -func (s *ProtocolServiceTestSuite) SetupTest() { - aliceDBPath := "/tmp/alice.db" - aliceDBKey := "alice" - bobDBPath := "/tmp/bob.db" - bobDBKey := "bob" - - os.Remove(aliceDBPath) - os.Remove(bobDBPath) - - alicePersistence, err := NewSQLLitePersistence(aliceDBPath, aliceDBKey) - if err != nil { - panic(err) - } - - bobPersistence, err := NewSQLLitePersistence(bobDBPath, bobDBKey) - if err != nil { - panic(err) - } - - addedBundlesHandler := func(addedBundles []*multidevice.Installation) {} - onNewSharedSecretHandler := func(secret []*sharedsecret.Secret) {} - - aliceMultideviceConfig := &multidevice.Config{ - MaxInstallations: 3, - InstallationID: "1", - ProtocolVersion: ProtocolVersion, - } - - s.alice = NewProtocolService( - NewEncryptionService(alicePersistence, DefaultEncryptionServiceConfig("1")), - sharedsecret.NewService(alicePersistence.GetSharedSecretStorage()), - multidevice.New(aliceMultideviceConfig, alicePersistence.GetMultideviceStorage()), - addedBundlesHandler, - onNewSharedSecretHandler, - ) - - bobMultideviceConfig := &multidevice.Config{ - MaxInstallations: 3, - InstallationID: "2", - ProtocolVersion: ProtocolVersion, - } - - s.bob = NewProtocolService( - NewEncryptionService(bobPersistence, DefaultEncryptionServiceConfig("2")), - sharedsecret.NewService(bobPersistence.GetSharedSecretStorage()), - multidevice.New(bobMultideviceConfig, bobPersistence.GetMultideviceStorage()), - addedBundlesHandler, - onNewSharedSecretHandler, - ) - -} - -func (s *ProtocolServiceTestSuite) TestBuildPublicMessage() { - aliceKey, err := crypto.GenerateKey() - s.NoError(err) - - payload := []byte("test") - s.NoError(err) - - msg, err := s.alice.BuildPublicMessage(aliceKey, payload) - s.NoError(err) - s.NotNil(msg, "It creates a message") - - s.NotNilf(msg.GetBundles(), "It adds a bundle to the message") -} - -func (s *ProtocolServiceTestSuite) TestBuildDirectMessage() { - bobKey, err := crypto.GenerateKey() - s.NoError(err) - aliceKey, err := crypto.GenerateKey() - s.NoError(err) - - payload := []byte("test") - - msgSpec, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, payload) - s.NoError(err) - s.NotNil(msgSpec, "It creates a message spec") - - msg := msgSpec.Message - s.NotNil(msg, "It creates a messages") - - s.NotNilf(msg.GetBundle(), "It adds a bundle to the message") - - directMessage := msg.GetDirectMessage() - s.NotNilf(directMessage, "It sets the direct message") - - encryptedPayload := directMessage["none"].GetPayload() - s.NotNilf(encryptedPayload, "It sets the payload of the message") - - s.NotEqualf(payload, encryptedPayload, "It encrypts the payload") -} - -func (s *ProtocolServiceTestSuite) TestBuildAndReadDirectMessage() { - bobKey, err := crypto.GenerateKey() - s.Require().NoError(err) - aliceKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - payload := []byte("test") - - // Message is sent with DH - msgSpec, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, payload) - s.Require().NoError(err) - s.Require().NotNil(msgSpec) - - msg := msgSpec.Message - s.Require().NotNil(msg) - - // Bob is able to decrypt the message - unmarshaledMsg, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, msg, []byte("message-id")) - s.NoError(err) - s.NotNil(unmarshaledMsg) - - recoveredPayload := []byte("test") - s.Equalf(payload, recoveredPayload, "It successfully unmarshal the decrypted message") -} - -func (s *ProtocolServiceTestSuite) TestSecretNegotiation() { - var secretResponse []*sharedsecret.Secret - bobKey, err := crypto.GenerateKey() - s.NoError(err) - aliceKey, err := crypto.GenerateKey() - s.NoError(err) - - payload := []byte("test") - - s.bob.onNewSharedSecretHandler = func(secret []*sharedsecret.Secret) { - secretResponse = secret - } - msgSpec, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, payload) - s.NoError(err) - s.NotNil(msgSpec, "It creates a message spec") - - bundle := msgSpec.Message.GetBundle() - s.Require().NotNil(bundle) - - signedPreKeys := bundle.GetSignedPreKeys() - s.Require().NotNil(signedPreKeys) - - signedPreKey := signedPreKeys["1"] - s.Require().NotNil(signedPreKey) - - s.Require().Equal(uint32(1), signedPreKey.GetProtocolVersion()) - - _, err = s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, msgSpec.Message, []byte("message-id")) - s.NoError(err) - - s.Require().NotNil(secretResponse) -} diff --git a/messaging/chat/sql_lite_persistence_keys_storage_test.go b/messaging/chat/sql_lite_persistence_keys_storage_test.go deleted file mode 100644 index a37b9cf7d..000000000 --- a/messaging/chat/sql_lite_persistence_keys_storage_test.go +++ /dev/null @@ -1,200 +0,0 @@ -package chat - -import ( - dr "github.com/status-im/doubleratchet" - "github.com/stretchr/testify/suite" - "os" - "testing" -) - -var ( - pubKey1 = dr.Key{0xe3, 0xbe, 0xb9, 0x4e, 0x70, 0x17, 0x37, 0xc, 0x1, 0x8f, 0xa9, 0x7e, 0xef, 0x4, 0xfb, 0x23, 0xac, 0xea, 0x28, 0xf7, 0xa9, 0x56, 0xcc, 0x1d, 0x46, 0xf3, 0xb5, 0x1d, 0x7d, 0x7d, 0x5e, 0x2c} - pubKey2 = dr.Key{0xec, 0x8, 0x10, 0x7c, 0x33, 0x54, 0x0, 0x20, 0xe9, 0x4f, 0x6c, 0x84, 0xe4, 0x39, 0x50, 0x5a, 0x2f, 0x60, 0xbe, 0x81, 0xa, 0x78, 0x8b, 0xeb, 0x1e, 0x2c, 0x9, 0x8d, 0x4b, 0x4d, 0xc1, 0x40} - mk1 = dr.Key{0x00, 0x8, 0x10, 0x7c, 0x33, 0x54, 0x0, 0x20, 0xe9, 0x4f, 0x6c, 0x84, 0xe4, 0x39, 0x50, 0x5a, 0x2f, 0x60, 0xbe, 0x81, 0xa, 0x78, 0x8b, 0xeb, 0x1e, 0x2c, 0x9, 0x8d, 0x4b, 0x4d, 0xc1, 0x40} - mk2 = dr.Key{0x01, 0x8, 0x10, 0x7c, 0x33, 0x54, 0x0, 0x20, 0xe9, 0x4f, 0x6c, 0x84, 0xe4, 0x39, 0x50, 0x5a, 0x2f, 0x60, 0xbe, 0x81, 0xa, 0x78, 0x8b, 0xeb, 0x1e, 0x2c, 0x9, 0x8d, 0x4b, 0x4d, 0xc1, 0x40} - mk3 = dr.Key{0x02, 0x8, 0x10, 0x7c, 0x33, 0x54, 0x0, 0x20, 0xe9, 0x4f, 0x6c, 0x84, 0xe4, 0x39, 0x50, 0x5a, 0x2f, 0x60, 0xbe, 0x81, 0xa, 0x78, 0x8b, 0xeb, 0x1e, 0x2c, 0x9, 0x8d, 0x4b, 0x4d, 0xc1, 0x40} - mk4 = dr.Key{0x03, 0x8, 0x10, 0x7c, 0x33, 0x54, 0x0, 0x20, 0xe9, 0x4f, 0x6c, 0x84, 0xe4, 0x39, 0x50, 0x5a, 0x2f, 0x60, 0xbe, 0x81, 0xa, 0x78, 0x8b, 0xeb, 0x1e, 0x2c, 0x9, 0x8d, 0x4b, 0x4d, 0xc1, 0x40} - mk5 = dr.Key{0x04, 0x8, 0x10, 0x7c, 0x33, 0x54, 0x0, 0x20, 0xe9, 0x4f, 0x6c, 0x84, 0xe4, 0x39, 0x50, 0x5a, 0x2f, 0x60, 0xbe, 0x81, 0xa, 0x78, 0x8b, 0xeb, 0x1e, 0x2c, 0x9, 0x8d, 0x4b, 0x4d, 0xc1, 0x40} -) - -func TestSQLLitePersistenceKeysStorageTestSuite(t *testing.T) { - suite.Run(t, new(SQLLitePersistenceKeysStorageTestSuite)) -} - -type SQLLitePersistenceKeysStorageTestSuite struct { - suite.Suite - service dr.KeysStorage -} - -func (s *SQLLitePersistenceKeysStorageTestSuite) SetupTest() { - dbPath := "/tmp/status-key-store.db" - key := "blahblahblah" - - os.Remove(dbPath) - - p, err := NewSQLLitePersistence(dbPath, key) - if err != nil { - panic(err) - } - s.service = p.GetKeysStorage() -} - -func (s *SQLLitePersistenceKeysStorageTestSuite) TestKeysStorageSqlLiteGetMissing() { - // Act. - _, ok, err := s.service.Get(pubKey1, 0) - - // Assert. - s.NoError(err) - s.False(ok, "It returns false") -} - -func (s *SQLLitePersistenceKeysStorageTestSuite) TestKeysStorageSqlLite_Put() { - // Act and assert. - err := s.service.Put([]byte("session-id"), pubKey1, 0, mk1, 1) - s.NoError(err) -} - -func (s *SQLLitePersistenceKeysStorageTestSuite) TestKeysStorageSqlLite_DeleteOldMks() { - // Insert keys out-of-order - err := s.service.Put([]byte("session-id"), pubKey1, 0, mk1, 1) - s.NoError(err) - err = s.service.Put([]byte("session-id"), pubKey1, 1, mk2, 2) - s.NoError(err) - err = s.service.Put([]byte("session-id"), pubKey1, 2, mk3, 20) - s.NoError(err) - err = s.service.Put([]byte("session-id"), pubKey1, 3, mk4, 21) - s.NoError(err) - err = s.service.Put([]byte("session-id"), pubKey1, 4, mk5, 22) - s.NoError(err) - - err = s.service.DeleteOldMks([]byte("session-id"), 20) - s.NoError(err) - - _, ok, err := s.service.Get(pubKey1, 0) - s.NoError(err) - s.False(ok) - - _, ok, err = s.service.Get(pubKey1, 1) - s.NoError(err) - s.False(ok) - - _, ok, err = s.service.Get(pubKey1, 2) - s.NoError(err) - s.False(ok) - - _, ok, err = s.service.Get(pubKey1, 3) - s.NoError(err) - s.True(ok) - - _, ok, err = s.service.Get(pubKey1, 4) - s.NoError(err) - s.True(ok) -} - -func (s *SQLLitePersistenceKeysStorageTestSuite) TestKeysStorageSqlLite_TruncateMks() { - // Insert keys out-of-order - err := s.service.Put([]byte("session-id"), pubKey2, 2, mk5, 5) - s.NoError(err) - err = s.service.Put([]byte("session-id"), pubKey2, 0, mk3, 3) - s.NoError(err) - err = s.service.Put([]byte("session-id"), pubKey1, 1, mk2, 2) - s.NoError(err) - err = s.service.Put([]byte("session-id"), pubKey2, 1, mk4, 4) - s.NoError(err) - err = s.service.Put([]byte("session-id"), pubKey1, 0, mk1, 1) - s.NoError(err) - - err = s.service.TruncateMks([]byte("session-id"), 2) - s.NoError(err) - - _, ok, err := s.service.Get(pubKey1, 0) - s.NoError(err) - s.False(ok) - - _, ok, err = s.service.Get(pubKey1, 1) - s.NoError(err) - s.False(ok) - - _, ok, err = s.service.Get(pubKey2, 0) - s.NoError(err) - s.False(ok) - - _, ok, err = s.service.Get(pubKey2, 1) - s.NoError(err) - s.True(ok) - - _, ok, err = s.service.Get(pubKey2, 2) - s.NoError(err) - s.True(ok) -} - -func (s *SQLLitePersistenceKeysStorageTestSuite) TestKeysStorageSqlLite_Count() { - - // Act. - cnt, err := s.service.Count(pubKey1) - - // Assert. - s.NoError(err) - s.EqualValues(0, cnt, "It returns 0 when no keys are in the database") -} - -func (s *SQLLitePersistenceKeysStorageTestSuite) TestKeysStorageSqlLite_Delete() { - // Arrange. - - // Act and assert. - err := s.service.DeleteMk(pubKey1, 0) - s.NoError(err) -} - -func (s *SQLLitePersistenceKeysStorageTestSuite) TestKeysStorageSqlLite_Flow() { - - // Act. - err := s.service.Put([]byte("session-id"), pubKey1, 0, mk1, 1) - s.NoError(err) - - k, ok, err := s.service.Get(pubKey1, 0) - - // Assert. - s.NoError(err) - s.True(ok, "It returns true") - s.Equal(mk1, k, "It returns the message key") - - // Act. - _, ok, err = s.service.Get(pubKey2, 0) - - // Assert. - s.NoError(err) - s.False(ok, "It returns false when querying non existing public key") - - // Act. - _, ok, err = s.service.Get(pubKey1, 1) - - // Assert. - s.NoError(err) - s.False(ok, "It returns false when querying the wrong msg number") - - // Act. - cnt, err := s.service.Count(pubKey1) - - // Assert. - s.NoError(err) - s.EqualValues(1, cnt) - - // Act and assert. - err = s.service.DeleteMk(pubKey1, 1) - s.NoError(err) - - // Act and assert. - err = s.service.DeleteMk(pubKey2, 0) - s.NoError(err) - - // Act. - err = s.service.DeleteMk(pubKey1, 0) - s.NoError(err) - - cnt, err = s.service.Count(pubKey1) - - // Assert. - s.NoError(err) - s.EqualValues(0, cnt) -} diff --git a/messaging/chat/sql_lite_persistence_test.go b/messaging/chat/sql_lite_persistence_test.go deleted file mode 100644 index 6f421883f..000000000 --- a/messaging/chat/sql_lite_persistence_test.go +++ /dev/null @@ -1,351 +0,0 @@ -package chat - -import ( - "database/sql" - "os" - "testing" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/status-im/status-go/messaging/multidevice" - "github.com/stretchr/testify/suite" -) - -const ( - dbPath = "/tmp/status-key-store.db" - key = "blahblahblah" -) - -func TestSQLLitePersistenceTestSuite(t *testing.T) { - suite.Run(t, new(SQLLitePersistenceTestSuite)) -} - -type SQLLitePersistenceTestSuite struct { - suite.Suite - // nolint: structcheck, megacheck - db *sql.DB - service Persistence -} - -func (s *SQLLitePersistenceTestSuite) SetupTest() { - os.Remove(dbPath) - - p, err := NewSQLLitePersistence(dbPath, key) - s.Require().NoError(err) - s.service = p -} - -func (s *SQLLitePersistenceTestSuite) TestMultipleInit() { - os.Remove(dbPath) - - _, err := NewSQLLitePersistence(dbPath, key) - s.Require().NoError(err) - - _, err = NewSQLLitePersistence(dbPath, key) - s.Require().NoError(err) -} - -func (s *SQLLitePersistenceTestSuite) TestPrivateBundle() { - installationID := "1" - - key, err := crypto.GenerateKey() - s.Require().NoError(err) - - actualKey, err := s.service.GetPrivateKeyBundle([]byte("non-existing")) - s.Require().NoError(err, "Error was not returned even though bundle is not there") - s.Nil(actualKey) - - anyPrivateBundle, err := s.service.GetAnyPrivateBundle([]byte("non-existing-id"), []*multidevice.Installation{{ID: installationID, Version: 1}}) - s.Require().NoError(err) - s.Nil(anyPrivateBundle) - - bundle, err := NewBundleContainer(key, installationID) - s.Require().NoError(err) - - err = s.service.AddPrivateBundle(bundle) - s.Require().NoError(err) - - bundleID := bundle.GetBundle().GetSignedPreKeys()[installationID].GetSignedPreKey() - - actualKey, err = s.service.GetPrivateKeyBundle(bundleID) - s.Require().NoError(err) - s.Equal(bundle.GetPrivateSignedPreKey(), actualKey, "It returns the same key") - - identity := crypto.CompressPubkey(&key.PublicKey) - anyPrivateBundle, err = s.service.GetAnyPrivateBundle(identity, []*multidevice.Installation{{ID: installationID, Version: 1}}) - s.Require().NoError(err) - s.NotNil(anyPrivateBundle) - s.Equal(bundle.GetBundle().GetSignedPreKeys()[installationID].SignedPreKey, anyPrivateBundle.GetBundle().GetSignedPreKeys()[installationID].SignedPreKey, "It returns the same bundle") -} - -func (s *SQLLitePersistenceTestSuite) TestPublicBundle() { - key, err := crypto.GenerateKey() - s.Require().NoError(err) - - actualBundle, err := s.service.GetPublicBundle(&key.PublicKey, []*multidevice.Installation{{ID: "1", Version: 1}}) - s.Require().NoError(err, "Error was not returned even though bundle is not there") - s.Nil(actualBundle) - - bundleContainer, err := NewBundleContainer(key, "1") - s.Require().NoError(err) - - bundle := bundleContainer.GetBundle() - err = s.service.AddPublicBundle(bundle) - s.Require().NoError(err) - - actualBundle, err = s.service.GetPublicBundle(&key.PublicKey, []*multidevice.Installation{{ID: "1", Version: 1}}) - s.Require().NoError(err) - s.Equal(bundle.GetIdentity(), actualBundle.GetIdentity(), "It sets the right identity") - s.Equal(bundle.GetSignedPreKeys(), actualBundle.GetSignedPreKeys(), "It sets the right prekeys") -} - -func (s *SQLLitePersistenceTestSuite) TestUpdatedBundle() { - key, err := crypto.GenerateKey() - s.Require().NoError(err) - - actualBundle, err := s.service.GetPublicBundle(&key.PublicKey, []*multidevice.Installation{{ID: "1", Version: 1}}) - s.Require().NoError(err, "Error was not returned even though bundle is not there") - s.Nil(actualBundle) - - // Create & add initial bundle - bundleContainer, err := NewBundleContainer(key, "1") - s.Require().NoError(err) - - bundle := bundleContainer.GetBundle() - err = s.service.AddPublicBundle(bundle) - s.Require().NoError(err) - - // Create & add a new bundle - bundleContainer, err = NewBundleContainer(key, "1") - s.Require().NoError(err) - bundle = bundleContainer.GetBundle() - // We set the version - bundle.GetSignedPreKeys()["1"].Version = 1 - - err = s.service.AddPublicBundle(bundle) - s.Require().NoError(err) - - actualBundle, err = s.service.GetPublicBundle(&key.PublicKey, []*multidevice.Installation{{ID: "1", Version: 1}}) - s.Require().NoError(err) - s.Equal(bundle.GetIdentity(), actualBundle.GetIdentity(), "It sets the right identity") - s.Equal(bundle.GetSignedPreKeys(), actualBundle.GetSignedPreKeys(), "It sets the right prekeys") -} - -func (s *SQLLitePersistenceTestSuite) TestOutOfOrderBundles() { - key, err := crypto.GenerateKey() - s.Require().NoError(err) - - actualBundle, err := s.service.GetPublicBundle(&key.PublicKey, []*multidevice.Installation{{ID: "1", Version: 1}}) - s.Require().NoError(err, "Error was not returned even though bundle is not there") - s.Nil(actualBundle) - - // Create & add initial bundle - bundleContainer, err := NewBundleContainer(key, "1") - s.Require().NoError(err) - - bundle1 := bundleContainer.GetBundle() - err = s.service.AddPublicBundle(bundle1) - s.Require().NoError(err) - - // Create & add a new bundle - bundleContainer, err = NewBundleContainer(key, "1") - s.Require().NoError(err) - - bundle2 := bundleContainer.GetBundle() - // We set the version - bundle2.GetSignedPreKeys()["1"].Version = 1 - - err = s.service.AddPublicBundle(bundle2) - s.Require().NoError(err) - - // Add again the initial bundle - err = s.service.AddPublicBundle(bundle1) - s.Require().NoError(err) - - actualBundle, err = s.service.GetPublicBundle(&key.PublicKey, []*multidevice.Installation{{ID: "1", Version: 1}}) - s.Require().NoError(err) - s.Equal(bundle2.GetIdentity(), actualBundle.GetIdentity(), "It sets the right identity") - s.Equal(bundle2.GetSignedPreKeys()["1"].GetVersion(), uint32(1)) - s.Equal(bundle2.GetSignedPreKeys()["1"].GetSignedPreKey(), actualBundle.GetSignedPreKeys()["1"].GetSignedPreKey(), "It sets the right prekeys") -} - -func (s *SQLLitePersistenceTestSuite) TestMultiplePublicBundle() { - key, err := crypto.GenerateKey() - s.Require().NoError(err) - - actualBundle, err := s.service.GetPublicBundle(&key.PublicKey, []*multidevice.Installation{{ID: "1", Version: 1}}) - s.Require().NoError(err, "Error was not returned even though bundle is not there") - s.Nil(actualBundle) - - bundleContainer, err := NewBundleContainer(key, "1") - s.Require().NoError(err) - - bundle := bundleContainer.GetBundle() - err = s.service.AddPublicBundle(bundle) - s.Require().NoError(err) - - // Adding it again does not throw an error - err = s.service.AddPublicBundle(bundle) - s.Require().NoError(err) - - // Adding a different bundle - bundleContainer, err = NewBundleContainer(key, "1") - s.Require().NoError(err) - // We set the version - bundle = bundleContainer.GetBundle() - bundle.GetSignedPreKeys()["1"].Version = 1 - - err = s.service.AddPublicBundle(bundle) - s.Require().NoError(err) - - // Returns the most recent bundle - actualBundle, err = s.service.GetPublicBundle(&key.PublicKey, []*multidevice.Installation{{ID: "1", Version: 1}}) - s.Require().NoError(err) - - s.Equal(bundle.GetIdentity(), actualBundle.GetIdentity(), "It sets the identity") - s.Equal(bundle.GetSignedPreKeys(), actualBundle.GetSignedPreKeys(), "It sets the signed pre keys") - -} - -func (s *SQLLitePersistenceTestSuite) TestMultiDevicePublicBundle() { - key, err := crypto.GenerateKey() - s.Require().NoError(err) - - actualBundle, err := s.service.GetPublicBundle(&key.PublicKey, []*multidevice.Installation{{ID: "1", Version: 1}}) - s.Require().NoError(err, "Error was not returned even though bundle is not there") - s.Nil(actualBundle) - - bundleContainer, err := NewBundleContainer(key, "1") - s.Require().NoError(err) - - bundle := bundleContainer.GetBundle() - err = s.service.AddPublicBundle(bundle) - s.Require().NoError(err) - - // Adding it again does not throw an error - err = s.service.AddPublicBundle(bundle) - s.Require().NoError(err) - - // Adding a different bundle from a different instlation id - bundleContainer, err = NewBundleContainer(key, "2") - s.Require().NoError(err) - - bundle = bundleContainer.GetBundle() - err = s.service.AddPublicBundle(bundle) - s.Require().NoError(err) - - // Returns the most recent bundle - actualBundle, err = s.service.GetPublicBundle(&key.PublicKey, - []*multidevice.Installation{ - {ID: "1", Version: 1}, - {ID: "2", Version: 1}, - }) - s.Require().NoError(err) - - s.Equal(bundle.GetIdentity(), actualBundle.GetIdentity(), "It sets the identity") - s.NotNil(actualBundle.GetSignedPreKeys()["1"]) - s.NotNil(actualBundle.GetSignedPreKeys()["2"]) -} - -func (s *SQLLitePersistenceTestSuite) TestRatchetInfoPrivateBundle() { - key, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Add a private bundle - bundle, err := NewBundleContainer(key, "2") - s.Require().NoError(err) - - err = s.service.AddPrivateBundle(bundle) - s.Require().NoError(err) - - err = s.service.AddRatchetInfo( - []byte("symmetric-key"), - []byte("their-public-key"), - bundle.GetBundle().GetSignedPreKeys()["2"].GetSignedPreKey(), - []byte("ephemeral-public-key"), - "1", - ) - s.Require().NoError(err) - - ratchetInfo, err := s.service.GetRatchetInfo(bundle.GetBundle().GetSignedPreKeys()["2"].GetSignedPreKey(), []byte("their-public-key"), "1") - - s.Require().NoError(err) - s.Require().NotNil(ratchetInfo) - s.NotNil(ratchetInfo.ID, "It adds an id") - s.Equal(ratchetInfo.PrivateKey, bundle.GetPrivateSignedPreKey(), "It returns the private key") - s.Equal(ratchetInfo.Sk, []byte("symmetric-key"), "It returns the symmetric key") - s.Equal(ratchetInfo.Identity, []byte("their-public-key"), "It returns the identity of the contact") - s.Equal(ratchetInfo.PublicKey, bundle.GetBundle().GetSignedPreKeys()["2"].GetSignedPreKey(), "It returns the public key of the bundle") - s.Equal(bundle.GetBundle().GetSignedPreKeys()["2"].GetSignedPreKey(), ratchetInfo.BundleID, "It returns the bundle id") - s.Equal([]byte("ephemeral-public-key"), ratchetInfo.EphemeralKey, "It returns the ratchet ephemeral key") - s.Equal("1", ratchetInfo.InstallationID, "It returns the right installation id") -} - -func (s *SQLLitePersistenceTestSuite) TestRatchetInfoPublicBundle() { - installationID := "1" - theirPublicKey := []byte("their-public-key") - key, err := crypto.GenerateKey() - s.Require().NoError(err) - - // Add a private bundle - bundle, err := NewBundleContainer(key, installationID) - s.Require().NoError(err) - - err = s.service.AddPublicBundle(bundle.GetBundle()) - s.Require().NoError(err) - - signedPreKey := bundle.GetBundle().GetSignedPreKeys()[installationID].GetSignedPreKey() - - err = s.service.AddRatchetInfo( - []byte("symmetric-key"), - theirPublicKey, - signedPreKey, - []byte("public-ephemeral-key"), - installationID, - ) - s.Require().NoError(err) - - ratchetInfo, err := s.service.GetRatchetInfo(signedPreKey, theirPublicKey, installationID) - - s.Require().NoError(err) - s.Require().NotNil(ratchetInfo, "It returns the ratchet info") - - s.NotNil(ratchetInfo.ID, "It adds an id") - s.Nil(ratchetInfo.PrivateKey, "It does not return the private key") - s.Equal(ratchetInfo.Sk, []byte("symmetric-key"), "It returns the symmetric key") - s.Equal(ratchetInfo.Identity, theirPublicKey, "It returns the identity of the contact") - s.Equal(ratchetInfo.PublicKey, signedPreKey, "It returns the public key of the bundle") - s.Equal(installationID, ratchetInfo.InstallationID, "It returns the right installationID") - s.Nilf(ratchetInfo.PrivateKey, "It does not return the private key") - - ratchetInfo, err = s.service.GetAnyRatchetInfo(theirPublicKey, installationID) - s.Require().NoError(err) - s.Require().NotNil(ratchetInfo, "It returns the ratchet info") - s.NotNil(ratchetInfo.ID, "It adds an id") - s.Nil(ratchetInfo.PrivateKey, "It does not return the private key") - s.Equal(ratchetInfo.Sk, []byte("symmetric-key"), "It returns the symmetric key") - s.Equal(ratchetInfo.Identity, theirPublicKey, "It returns the identity of the contact") - s.Equal(ratchetInfo.PublicKey, signedPreKey, "It returns the public key of the bundle") - s.Equal(signedPreKey, ratchetInfo.BundleID, "It returns the bundle id") - s.Equal(installationID, ratchetInfo.InstallationID, "It saves the right installation ID") -} - -func (s *SQLLitePersistenceTestSuite) TestRatchetInfoNoBundle() { - err := s.service.AddRatchetInfo( - []byte("symmetric-key"), - []byte("their-public-key"), - []byte("non-existing-bundle"), - []byte("non-existing-ephemeral-key"), - "none", - ) - - s.Error(err, "It returns an error") - - _, err = s.service.GetRatchetInfo([]byte("non-existing-bundle"), []byte("their-public-key"), "none") - s.Require().NoError(err) - - ratchetInfo, err := s.service.GetAnyRatchetInfo([]byte("their-public-key"), "4") - s.Require().NoError(err) - s.Nil(ratchetInfo, "It returns nil when no bundle is there") -} - -// TODO: Add test for MarkBundleExpired diff --git a/messaging/chat/x3dh_test.go b/messaging/chat/x3dh_test.go deleted file mode 100644 index d68c4f9d3..000000000 --- a/messaging/chat/x3dh_test.go +++ /dev/null @@ -1,210 +0,0 @@ -package chat - -import ( - "fmt" - "testing" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/crypto/ecies" - "github.com/status-im/status-go/messaging/chat/protobuf" - "github.com/stretchr/testify/require" -) - -const ( - alicePrivateKey = "00000000000000000000000000000000" - aliceEphemeralKey = "11111111111111111111111111111111" - bobPrivateKey = "22222222222222222222222222222222" - bobSignedPreKey = "33333333333333333333333333333333" -) - -var sharedKey = []byte{0xa4, 0xe9, 0x23, 0xd0, 0xaf, 0x8f, 0xe7, 0x8a, 0x5, 0x63, 0x63, 0xbe, 0x20, 0xe7, 0x1c, 0xa, 0x58, 0xe5, 0x69, 0xea, 0x8f, 0xc1, 0xf7, 0x92, 0x89, 0xec, 0xa1, 0xd, 0x9f, 0x68, 0x13, 0x3a} - -func bobBundle() (*protobuf.Bundle, error) { - privateKey, err := crypto.ToECDSA([]byte(bobPrivateKey)) - if err != nil { - return nil, err - } - - signedPreKey, err := crypto.ToECDSA([]byte(bobSignedPreKey)) - if err != nil { - return nil, err - } - - compressedPreKey := crypto.CompressPubkey(&signedPreKey.PublicKey) - - signature, err := crypto.Sign(crypto.Keccak256(compressedPreKey), privateKey) - if err != nil { - return nil, err - } - - signedPreKeys := make(map[string]*protobuf.SignedPreKey) - signedPreKeys[bobInstallationID] = &protobuf.SignedPreKey{SignedPreKey: compressedPreKey} - - bundle := protobuf.Bundle{ - Identity: crypto.CompressPubkey(&privateKey.PublicKey), - SignedPreKeys: signedPreKeys, - Signature: signature, - } - - return &bundle, nil -} - -func TestNewBundleContainer(t *testing.T) { - privateKey, err := crypto.ToECDSA([]byte(alicePrivateKey)) - require.NoError(t, err, "Private key should be generated without errors") - - bundleContainer, err := NewBundleContainer(privateKey, bobInstallationID) - require.NoError(t, err, "Bundle container should be created successfully") - - err = SignBundle(privateKey, bundleContainer) - require.NoError(t, err, "Bundle container should be signed successfully") - - require.NoError(t, err, "Bundle container should be created successfully") - - bundle := bundleContainer.Bundle - require.NotNil(t, bundle, "Bundle should be generated without errors") - - signatureMaterial := append([]byte(bobInstallationID), bundle.GetSignedPreKeys()[bobInstallationID].GetSignedPreKey()...) - signatureMaterial = append(signatureMaterial, []byte("0")...) - signatureMaterial = append(signatureMaterial, []byte(fmt.Sprint(bundle.GetTimestamp()))...) - recoveredPublicKey, err := crypto.SigToPub( - crypto.Keccak256(signatureMaterial), - bundle.Signature, - ) - - require.NoError(t, err, "Public key should be recovered from the bundle successfully") - - require.Equal( - t, - privateKey.PublicKey, - *recoveredPublicKey, - "The correct public key should be recovered", - ) -} - -func TestSignBundle(t *testing.T) { - privateKey, err := crypto.ToECDSA([]byte(alicePrivateKey)) - require.NoError(t, err, "Private key should be generated without errors") - - bundleContainer1, err := NewBundleContainer(privateKey, "1") - require.NoError(t, err, "Bundle container should be created successfully") - - bundle1 := bundleContainer1.Bundle - require.NotNil(t, bundle1, "Bundle should be generated without errors") - - // We add a signed pre key - signedPreKeys := bundle1.GetSignedPreKeys() - signedPreKeys["2"] = &protobuf.SignedPreKey{SignedPreKey: []byte("key")} - - err = SignBundle(privateKey, bundleContainer1) - require.NoError(t, err) - - signatureMaterial := append([]byte("1"), bundle1.GetSignedPreKeys()["1"].GetSignedPreKey()...) - signatureMaterial = append(signatureMaterial, []byte("0")...) - signatureMaterial = append(signatureMaterial, []byte("2")...) - signatureMaterial = append(signatureMaterial, []byte("key")...) - signatureMaterial = append(signatureMaterial, []byte("0")...) - signatureMaterial = append(signatureMaterial, []byte(fmt.Sprint(bundle1.GetTimestamp()))...) - - recoveredPublicKey, err := crypto.SigToPub( - crypto.Keccak256(signatureMaterial), - bundleContainer1.GetBundle().Signature, - ) - - require.NoError(t, err, "Public key should be recovered from the bundle successfully") - - require.Equal( - t, - privateKey.PublicKey, - *recoveredPublicKey, - "The correct public key should be recovered", - ) -} - -func TestExtractIdentity(t *testing.T) { - privateKey, err := crypto.ToECDSA([]byte(alicePrivateKey)) - require.NoError(t, err, "Private key should be generated without errors") - - bundleContainer, err := NewBundleContainer(privateKey, "1") - require.NoError(t, err, "Bundle container should be created successfully") - - err = SignBundle(privateKey, bundleContainer) - require.NoError(t, err, "Bundle container should be signed successfully") - - bundle := bundleContainer.Bundle - require.NotNil(t, bundle, "Bundle should be generated without errors") - - recoveredPublicKey, err := ExtractIdentity(bundle) - - require.NoError(t, err, "Public key should be recovered from the bundle successfully") - - require.Equal( - t, - privateKey.PublicKey, - *recoveredPublicKey, - "The correct public key should be recovered", - ) -} - -// Alice wants to send a message to Bob -func TestX3dhActive(t *testing.T) { - bobIdentityKey, err := crypto.ToECDSA([]byte(bobPrivateKey)) - require.NoError(t, err, "Bundle identity key should be generated without errors") - - bobSignedPreKey, err := crypto.ToECDSA([]byte(bobSignedPreKey)) - require.NoError(t, err, "Bundle signed pre key should be generated without errors") - - aliceIdentityKey, err := crypto.ToECDSA([]byte(alicePrivateKey)) - require.NoError(t, err, "Private key should be generated without errors") - - aliceEphemeralKey, err := crypto.ToECDSA([]byte(aliceEphemeralKey)) - require.NoError(t, err, "Ephemeral key should be generated without errors") - - x3dh, err := x3dhActive( - ecies.ImportECDSA(aliceIdentityKey), - ecies.ImportECDSAPublic(&bobSignedPreKey.PublicKey), - ecies.ImportECDSA(aliceEphemeralKey), - ecies.ImportECDSAPublic(&bobIdentityKey.PublicKey), - ) - require.NoError(t, err, "Shared key should be generated without errors") - require.Equal(t, sharedKey, x3dh, "Should generate the correct key") -} - -// Bob receives a message from Alice -func TestPerformPassiveX3DH(t *testing.T) { - alicePrivateKey, err := crypto.ToECDSA([]byte(alicePrivateKey)) - require.NoError(t, err, "Private key should be generated without errors") - - bobSignedPreKey, err := crypto.ToECDSA([]byte(bobSignedPreKey)) - require.NoError(t, err, "Private key should be generated without errors") - - aliceEphemeralKey, err := crypto.ToECDSA([]byte(aliceEphemeralKey)) - require.NoError(t, err, "Ephemeral key should be generated without errors") - - bobPrivateKey, err := crypto.ToECDSA([]byte(bobPrivateKey)) - require.NoError(t, err, "Private key should be generated without errors") - - x3dh, err := PerformPassiveX3DH( - &alicePrivateKey.PublicKey, - bobSignedPreKey, - &aliceEphemeralKey.PublicKey, - bobPrivateKey, - ) - require.NoError(t, err, "Shared key should be generated without errors") - require.Equal(t, sharedKey, x3dh, "Should generate the correct key") -} - -func TestPerformActiveX3DH(t *testing.T) { - bundle, err := bobBundle() - require.NoError(t, err, "Test bundle should be generated without errors") - - privateKey, err := crypto.ToECDSA([]byte(bobPrivateKey)) - require.NoError(t, err, "Private key should be imported without errors") - - signedPreKey := bundle.GetSignedPreKeys()[bobInstallationID].GetSignedPreKey() - - actualSharedSecret, actualEphemeralKey, err := PerformActiveX3DH(bundle.GetIdentity(), signedPreKey, privateKey) - require.NoError(t, err, "No error should be reported") - require.NotNil(t, actualEphemeralKey, "An ephemeral key-pair should be generated") - require.NotNil(t, actualSharedSecret, "A shared key should be generated") -} diff --git a/messaging/filter/service.go b/messaging/filter/service.go deleted file mode 100644 index 0860d8c37..000000000 --- a/messaging/filter/service.go +++ /dev/null @@ -1,594 +0,0 @@ -package filter - -import ( - "crypto/ecdsa" - "encoding/hex" - "errors" - "fmt" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - "github.com/status-im/status-go/messaging/sharedsecret" - whisper "github.com/status-im/whisper/whisperv6" - "math/big" - "sync" - "time" -) - -const ( - discoveryTopic = "contact-discovery" -) - -// The number of partitions -var nPartitions = big.NewInt(5000) -var minPow = 0.0 - -type Filter struct { - FilterID string - Topic whisper.TopicType - SymKeyID string -} - -type Chat struct { - // ChatID is the identifier of the chat - ChatID string `json:"chatId"` - // SymKeyID is the symmetric key id used for symmetric chats - SymKeyID string `json:"symKeyId"` - // OneToOne tells us if we need to use asymmetric encryption for this chat - OneToOne bool `json:"oneToOne"` - // Listen is whether we are actually listening for messages on this chat, or the filter is only created in order to be able to post on the topic - Listen bool `json:"listen"` - // FilterID the whisper filter id generated - FilterID string `json:"filterId"` - // Identity is the public key of the other recipient for non-public chats - Identity string `json:"identity"` - // Topic is the whisper topic - Topic whisper.TopicType `json:"topic"` - // Discovery is whether this is a discovery topic - Discovery bool `json:"discovery"` - // Negotiated tells us whether is a negotiated topic - Negotiated bool `json:"negotiated"` -} - -type Messages struct { - Chat *Chat `json:"chat"` - Messages []*whisper.Message `json:"messages"` - Error error `json:"error"` -} - -type Service struct { - whisper *whisper.Whisper - secret *sharedsecret.Service - chats map[string]*Chat - persistence Persistence - mutex sync.Mutex - keys map[string][]byte - quit chan struct{} - onNewMessages func([]*Messages) -} - -// New returns a new filter service -func New(w *whisper.Whisper, p Persistence, s *sharedsecret.Service, onNewMessages func([]*Messages)) *Service { - return &Service{ - whisper: w, - secret: s, - mutex: sync.Mutex{}, - persistence: p, - chats: make(map[string]*Chat), - quit: make(chan struct{}), - onNewMessages: onNewMessages, - } -} - -// LoadChat should return a list of newly chats loaded -func (s *Service) Init(chats []*Chat) ([]*Chat, error) { - log.Debug("Initializing filter service", "chats", chats) - - keys, err := s.persistence.All() - if err != nil { - return nil, err - } - s.keys = keys - - keyID := s.whisper.SelectedKeyPairID() - if keyID == "" { - return nil, errors.New("no key selected") - } - myKey, err := s.whisper.GetPrivateKey(keyID) - if err != nil { - return nil, err - } - - // Add our own topic - log.Debug("Loading one to one chats") - identityStr := fmt.Sprintf("%x", crypto.FromECDSAPub(&myKey.PublicKey)) - _, err = s.loadOneToOne(myKey, identityStr, true) - if err != nil { - log.Error("Error loading one to one chats", "err", err) - - return nil, err - } - - // Add discovery topic - log.Debug("Loading discovery topics") - err = s.loadDiscovery(myKey) - if err != nil { - return nil, err - } - - // Add the various one to one and public chats - log.Debug("Loading chats") - for _, chat := range chats { - _, err = s.load(myKey, chat) - if err != nil { - return nil, err - } - } - - // Add the negotiated secrets - log.Debug("Loading negotiated topics") - secrets, err := s.secret.All() - if err != nil { - return nil, err - } - - for _, secret := range secrets { - if _, err := s.ProcessNegotiatedSecret(secret); err != nil { - return nil, err - } - } - - s.mutex.Lock() - defer s.mutex.Unlock() - - var allChats []*Chat - for _, chat := range s.chats { - allChats = append(allChats, chat) - } - log.Debug("Loaded chats") - return allChats, nil -} - -func (s *Service) Start(checkPeriod time.Duration) { - ticker := time.NewTicker(checkPeriod) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - messages := s.getMessages() - - if len(messages) != 0 { - s.onNewMessages(messages) - } - case <-s.quit: - return - } - } -} - -// Stop removes all the filters -func (s *Service) Stop() error { - close(s.quit) - - var chats []*Chat - - s.mutex.Lock() - for _, chat := range s.chats { - chats = append(chats, chat) - } - s.mutex.Unlock() - - return s.Remove(chats) -} - -// Remove remove all the filters associated with a chat/identity -func (s *Service) Remove(chats []*Chat) error { - s.mutex.Lock() - defer s.mutex.Unlock() - log.Debug("Removing chats", "chats", chats) - - for _, chat := range chats { - log.Debug("Removing chat", "chat", chat) - if err := s.whisper.Unsubscribe(chat.FilterID); err != nil { - return err - } - if chat.SymKeyID != "" { - s.whisper.DeleteSymKey(chat.SymKeyID) - } - delete(s.chats, chat.ChatID) - } - - return nil -} - -// LoadPartitioned creates a filter for a partitioned topic -func (s *Service) LoadPartitioned(myKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, listen bool) (*Chat, error) { - s.mutex.Lock() - defer s.mutex.Unlock() - - chatID := PublicKeyToPartitionedTopic(theirPublicKey) - - if _, ok := s.chats[chatID]; ok { - return s.chats[chatID], nil - } - - // We set up a filter so we can publish, but we discard envelopes if listen is false - filter, err := s.addAsymmetricFilter(myKey, chatID, listen) - if err != nil { - return nil, err - } - - identityStr := fmt.Sprintf("%x", crypto.FromECDSAPub(theirPublicKey)) - - chat := &Chat{ - ChatID: chatID, - FilterID: filter.FilterID, - Topic: filter.Topic, - Listen: listen, - Identity: identityStr, - Discovery: true, - } - - s.chats[chatID] = chat - - return chat, nil -} - -// Load creates filters for a given chat, and returns all the created filters -func (s *Service) Load(chat *Chat) ([]*Chat, error) { - keyID := s.whisper.SelectedKeyPairID() - if keyID == "" { - return nil, errors.New("no key selected") - } - myKey, err := s.whisper.GetPrivateKey(keyID) - - if err != nil { - return nil, err - } - return s.load(myKey, chat) -} - -func ContactCodeTopic(identity string) string { - return "0x" + identity + "-contact-code" -} - -// Get returns a negotiated chat given an identity -func (s *Service) GetNegotiated(identity *ecdsa.PublicKey) *Chat { - s.mutex.Lock() - defer s.mutex.Unlock() - - return s.chats[negotiatedID(identity)] -} - -// GetByID returns a chat by chatID -func (s *Service) GetByID(chatID string) *Chat { - s.mutex.Lock() - defer s.mutex.Unlock() - - return s.chats[chatID] -} - -// ProcessNegotiatedSecret adds a filter based on the agreed secret -func (s *Service) ProcessNegotiatedSecret(secret *sharedsecret.Secret) (*Chat, error) { - s.mutex.Lock() - defer s.mutex.Unlock() - - chatID := negotiatedID(secret.Identity) - // If we already have a chat do nothing - if _, ok := s.chats[chatID]; ok { - return s.chats[chatID], nil - } - - keyString := fmt.Sprintf("%x", secret.Key) - filter, err := s.addSymmetric(keyString) - if err != nil { - return nil, err - } - - identityStr := fmt.Sprintf("%x", crypto.FromECDSAPub(secret.Identity)) - - chat := &Chat{ - ChatID: chatID, - Topic: filter.Topic, - SymKeyID: filter.SymKeyID, - FilterID: filter.FilterID, - Identity: identityStr, - Listen: true, - Negotiated: true, - } - - log.Debug("Processing negotiated secret", "chat-id", chatID, "topic", filter.Topic) - - s.chats[chat.ChatID] = chat - return chat, nil -} - -// ToTopic converts a string to a whisper topic -func ToTopic(s string) []byte { - return crypto.Keccak256([]byte(s))[:whisper.TopicLength] -} - -// PublicKeyToPartitionedTopic returns the associated partitioned topic string -// with the given public key -func PublicKeyToPartitionedTopic(publicKey *ecdsa.PublicKey) string { - partition := big.NewInt(0) - partition.Mod(publicKey.X, nPartitions) - return fmt.Sprintf("contact-discovery-%d", partition.Int64()) -} - -// PublicKeyToPartitionedTopicBytes returns the bytes of the partitioned topic -// associated with the given public key -func PublicKeyToPartitionedTopicBytes(publicKey *ecdsa.PublicKey) []byte { - return ToTopic(PublicKeyToPartitionedTopic(publicKey)) -} - -// loadDiscovery adds the discovery filter -func (s *Service) loadDiscovery(myKey *ecdsa.PrivateKey) error { - s.mutex.Lock() - defer s.mutex.Unlock() - - if _, ok := s.chats[discoveryTopic]; ok { - return nil - } - - identityStr := fmt.Sprintf("%x", crypto.FromECDSAPub(&myKey.PublicKey)) - - discoveryChat := &Chat{ - ChatID: discoveryTopic, - Listen: true, - Identity: identityStr, - Discovery: true, - } - - discoveryResponse, err := s.addAsymmetricFilter(myKey, discoveryChat.ChatID, true) - if err != nil { - return err - } - - discoveryChat.Topic = discoveryResponse.Topic - discoveryChat.FilterID = discoveryResponse.FilterID - - s.chats[discoveryChat.ChatID] = discoveryChat - - // Load personal discovery - personalDiscoveryTopic := fmt.Sprintf("contact-discovery-%s", identityStr) - personalDiscoveryChat := &Chat{ - ChatID: personalDiscoveryTopic, - Listen: true, - Identity: identityStr, - Discovery: true, - } - - discoveryResponse, err = s.addAsymmetricFilter(myKey, personalDiscoveryChat.ChatID, true) - if err != nil { - return err - } - - personalDiscoveryChat.Topic = discoveryResponse.Topic - personalDiscoveryChat.FilterID = discoveryResponse.FilterID - - s.chats[personalDiscoveryChat.ChatID] = personalDiscoveryChat - - return nil -} - -// loadPublic adds a filter for a public chat -func (s *Service) loadPublic(chat *Chat) error { - s.mutex.Lock() - defer s.mutex.Unlock() - - if _, ok := s.chats[chat.ChatID]; ok { - return nil - } - - filterAndTopic, err := s.addSymmetric(chat.ChatID) - if err != nil { - return err - } - - chat.FilterID = filterAndTopic.FilterID - chat.SymKeyID = filterAndTopic.SymKeyID - chat.Topic = filterAndTopic.Topic - chat.Listen = true - - s.chats[chat.ChatID] = chat - return nil -} - -// loadOneToOne creates two filters for a given chat, one listening to the contact codes -// and another on the partitioned topic, if listen is specified. -func (s *Service) loadOneToOne(myKey *ecdsa.PrivateKey, identity string, listen bool) ([]*Chat, error) { - var chats []*Chat - contactCodeChat, err := s.loadContactCode(identity) - if err != nil { - return nil, err - } - - chats = append(chats, contactCodeChat) - - if listen { - publicKeyBytes, err := hex.DecodeString(identity) - if err != nil { - return nil, err - } - - publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes) - if err != nil { - return nil, err - } - - partitionedChat, err := s.LoadPartitioned(myKey, publicKey, listen) - if err != nil { - return nil, err - } - - chats = append(chats, partitionedChat) - } - return chats, nil -} - -// loadContactCode creates a filter for the topic are advertised for a given identity -func (s *Service) loadContactCode(identity string) (*Chat, error) { - s.mutex.Lock() - defer s.mutex.Unlock() - - chatID := ContactCodeTopic(identity) - if _, ok := s.chats[chatID]; ok { - return s.chats[chatID], nil - } - - contactCodeFilter, err := s.addSymmetric(chatID) - if err != nil { - return nil, err - } - chat := &Chat{ - ChatID: chatID, - FilterID: contactCodeFilter.FilterID, - Topic: contactCodeFilter.Topic, - SymKeyID: contactCodeFilter.SymKeyID, - Identity: identity, - Listen: true, - } - - s.chats[chatID] = chat - return chat, nil -} - -// addSymmetric adds a symmetric key filter -func (s *Service) addSymmetric(chatID string) (*Filter, error) { - var symKeyID string - var err error - - topic := ToTopic(chatID) - topics := [][]byte{topic} - - symKey, ok := s.keys[chatID] - if ok { - log.Debug("Loading from cache", "chat-id", chatID) - symKeyID, err = s.whisper.AddSymKeyDirect(symKey) - if err != nil { - log.Error("Adding symkey failed", "err", err) - return nil, err - } - } else { - log.Debug("Generating symkey", "chat-id", chatID) - symKeyID, err = s.whisper.AddSymKeyFromPassword(chatID) - if err != nil { - log.Error("Adding symkey from password failed", "err", err) - return nil, err - } - if symKey, err = s.whisper.GetSymKey(symKeyID); err != nil { - return nil, err - } - s.keys[chatID] = symKey - - err = s.persistence.Add(chatID, symKey) - if err != nil { - return nil, err - } - } - - f := &whisper.Filter{ - KeySym: symKey, - PoW: minPow, - AllowP2P: true, - Topics: topics, - Messages: s.whisper.NewMessageStore(), - } - - id, err := s.whisper.Subscribe(f) - if err != nil { - return nil, err - } - - return &Filter{ - FilterID: id, - SymKeyID: symKeyID, - Topic: whisper.BytesToTopic(topic), - }, nil -} - -// addAsymmetricFilter adds a filter with our privatekey, and set minPow according to the listen parameter -func (s *Service) addAsymmetricFilter(keyAsym *ecdsa.PrivateKey, chatID string, listen bool) (*Filter, error) { - var err error - var pow float64 - - if listen { - pow = minPow - } else { - // Set high pow so we discard messages - pow = 1 - } - - topic := ToTopic(chatID) - topics := [][]byte{topic} - - f := &whisper.Filter{ - KeyAsym: keyAsym, - PoW: pow, - AllowP2P: true, - Topics: topics, - Messages: s.whisper.NewMessageStore(), - } - - id, err := s.whisper.Subscribe(f) - if err != nil { - return nil, err - } - - return &Filter{FilterID: id, Topic: whisper.BytesToTopic(topic)}, nil -} - -func (s *Service) getMessages() []*Messages { - var response []*Messages - s.mutex.Lock() - defer s.mutex.Unlock() - - for chatID := range s.chats { - messages := s.getMessagesForChat(chatID) - if messages.Error != nil || len(messages.Messages) != 0 { - response = append(response, messages) - } - } - - return response -} - -func (s *Service) getMessagesForChat(chatID string) *Messages { - response := &Messages{} - - response.Chat = s.chats[chatID] - if response.Chat == nil { - response.Error = errors.New("Chat not found") - - return response - } - - filter := s.whisper.GetFilter(response.Chat.FilterID) - if filter == nil { - response.Error = errors.New("Filter not found") - return response - } - - receivedMessages := filter.Retrieve() - response.Messages = make([]*whisper.Message, 0, len(receivedMessages)) - for _, msg := range receivedMessages { - response.Messages = append(response.Messages, whisper.ToWhisperMessage(msg)) - } - - return response -} - -func negotiatedID(identity *ecdsa.PublicKey) string { - return fmt.Sprintf("0x%x-negotiated", crypto.FromECDSAPub(identity)) -} - -func (s *Service) load(myKey *ecdsa.PrivateKey, chat *Chat) ([]*Chat, error) { - log.Debug("Loading chat", "chatID", chat.ChatID) - - if chat.OneToOne { - return s.loadOneToOne(myKey, chat.Identity, false) - - } - return []*Chat{chat}, s.loadPublic(chat) -} diff --git a/messaging/filter/service_test.go b/messaging/filter/service_test.go deleted file mode 100644 index 177c1b2a6..000000000 --- a/messaging/filter/service_test.go +++ /dev/null @@ -1,233 +0,0 @@ -package filter - -import ( - "crypto/ecdsa" - "fmt" - "io/ioutil" - "os" - "testing" - - "github.com/ethereum/go-ethereum/crypto" - whisper "github.com/status-im/whisper/whisperv6" - "github.com/stretchr/testify/suite" - - msgdb "github.com/status-im/status-go/messaging/db" - "github.com/status-im/status-go/messaging/sharedsecret" -) - -func TestServiceTestSuite(t *testing.T) { - suite.Run(t, new(ServiceTestSuite)) -} - -type TestKey struct { - privateKey *ecdsa.PrivateKey - partitionedTopic int -} - -func NewTestKey(privateKey string, partitionedTopic int) (*TestKey, error) { - key, err := crypto.HexToECDSA(privateKey) - if err != nil { - return nil, err - } - - return &TestKey{ - privateKey: key, - partitionedTopic: partitionedTopic, - }, nil - -} - -func (t *TestKey) PublicKeyString() string { - return fmt.Sprintf("%x", crypto.FromECDSAPub(&t.privateKey.PublicKey)) -} - -type ServiceTestSuite struct { - suite.Suite - service *Service - path string - keys []*TestKey -} - -func (s *ServiceTestSuite) SetupTest() { - keyStrs := []string{"c6cbd7d76bc5baca530c875663711b947efa6a86a900a9e8645ce32e5821484e", "d51dd64ad19ea84968a308dca246012c00d2b2101d41bce740acd1c650acc509"} - keyTopics := []int{4490, 3991} - - dbFile, err := ioutil.TempFile(os.TempDir(), "filter") - - s.Require().NoError(err) - s.path = dbFile.Name() - - for i, k := range keyStrs { - testKey, err := NewTestKey(k, keyTopics[i]) - s.Require().NoError(err) - - s.keys = append(s.keys, testKey) - } - - db, err := msgdb.Open(s.path, "", 0) - s.Require().NoError(err) - - // Build services - sharedSecretService := sharedsecret.NewService(sharedsecret.NewSQLLitePersistence(db)) - whisper := whisper.New(nil) - _, err = whisper.AddKeyPair(s.keys[0].privateKey) - s.Require().NoError(err) - - persistence := NewSQLLitePersistence(db) - - s.service = New(whisper, persistence, sharedSecretService, func([]*Messages) {}) -} - -func (s *ServiceTestSuite) TearDownTest() { - os.Remove(s.path) -} - -func (s *ServiceTestSuite) TestDiscoveryAndPartitionedTopic() { - chats := []*Chat{} - partitionedTopic := fmt.Sprintf("contact-discovery-%d", s.keys[0].partitionedTopic) - personalDiscoveryTopic := fmt.Sprintf("contact-discovery-%s", s.keys[0].PublicKeyString()) - contactCodeTopic := "0x" + s.keys[0].PublicKeyString() + "-contact-code" - - _, err := s.service.Init(chats) - s.Require().NoError(err) - - s.Require().Equal(4, len(s.service.chats), "It creates four filters") - - discoveryFilter := s.service.chats[discoveryTopic] - s.Require().NotNil(discoveryFilter, "It adds the discovery filter") - s.Require().True(discoveryFilter.Listen) - - personalDiscoveryFilter := s.service.chats[personalDiscoveryTopic] - s.Require().NotNil(personalDiscoveryFilter, "It adds the discovery filter") - s.Require().True(personalDiscoveryFilter.Listen) - - contactCodeFilter := s.service.chats[contactCodeTopic] - s.Require().NotNil(contactCodeFilter, "It adds the contact code filter") - s.Require().True(contactCodeFilter.Listen) - - partitionedFilter := s.service.chats[partitionedTopic] - s.Require().NotNil(partitionedFilter, "It adds the partitioned filter") - s.Require().True(partitionedFilter.Listen) -} - -func (s *ServiceTestSuite) TestPublicAndOneToOneChats() { - chats := []*Chat{ - { - ChatID: "status", - }, - { - ChatID: s.keys[1].PublicKeyString(), - Identity: s.keys[1].PublicKeyString(), - OneToOne: true, - }, - } - contactCodeTopic := "0x" + s.keys[1].PublicKeyString() + "-contact-code" - - response, err := s.service.Init(chats) - s.Require().NoError(err) - - actualChats := make(map[string]*Chat) - - for _, chat := range response { - actualChats[chat.ChatID] = chat - } - - s.Require().Equal(6, len(actualChats), "It creates two additional filters for the one to one and one for the public chat") - - statusFilter := actualChats["status"] - s.Require().NotNil(statusFilter, "It creates a filter for the public chat") - s.Require().NotNil(statusFilter.SymKeyID, "It returns a sym key id") - s.Require().True(statusFilter.Listen) - - contactCodeFilter := actualChats[contactCodeTopic] - s.Require().NotNil(contactCodeFilter, "It adds the contact code filter") - s.Require().True(contactCodeFilter.Listen) -} - -func (s *ServiceTestSuite) TestLoadFromCache() { - chats := []*Chat{ - { - ChatID: "status", - }, - { - ChatID: "status-1", - }, - } - _, err := s.service.Init(chats) - s.Require().NoError(err) - - // We create another service using the same persistence - service2 := New(s.service.whisper, s.service.persistence, s.service.secret, func([]*Messages) {}) - _, err = service2.Init(chats) - s.Require().NoError(err) -} - -func (s *ServiceTestSuite) TestNegotiatedTopic() { - chats := []*Chat{} - - negotiatedTopic1 := "0x" + s.keys[0].PublicKeyString() + "-negotiated" - negotiatedTopic2 := "0x" + s.keys[1].PublicKeyString() + "-negotiated" - - // We send a message to ourselves - _, _, err := s.service.secret.Send(s.keys[0].privateKey, "0-1", &s.keys[0].privateKey.PublicKey, []string{"0-2"}) - s.Require().NoError(err) - - // We send a message to someone else - _, _, err = s.service.secret.Send(s.keys[0].privateKey, "0-1", &s.keys[1].privateKey.PublicKey, []string{"0-2"}) - s.Require().NoError(err) - - response, err := s.service.Init(chats) - s.Require().NoError(err) - - actualChats := make(map[string]*Chat) - - for _, chat := range response { - actualChats[chat.ChatID] = chat - } - - s.Require().Equal(6, len(actualChats), "It creates two additional filters for the negotiated topics") - - negotiatedFilter1 := actualChats[negotiatedTopic1] - s.Require().NotNil(negotiatedFilter1, "It adds the negotiated filter") - negotiatedFilter2 := actualChats[negotiatedTopic2] - s.Require().NotNil(negotiatedFilter2, "It adds the negotiated filter") -} - -func (s *ServiceTestSuite) TestLoadChat() { - chats := []*Chat{} - - _, err := s.service.Init(chats) - s.Require().NoError(err) - - // We add a public chat - response1, err := s.service.Load(&Chat{ChatID: "status"}) - - s.Require().NoError(err) - s.Require().Equal(1, len(response1)) - s.Require().Equal("status", response1[0].ChatID) - s.Require().True(response1[0].Listen) -} - -func (s *ServiceTestSuite) TestNoInstallationIDs() { - chats := []*Chat{} - - negotiatedTopic1 := "0x" + s.keys[1].PublicKeyString() + "-negotiated" - - // We send a message to someone else, but without any installation ID - _, _, err := s.service.secret.Send(s.keys[0].privateKey, "0-1", &s.keys[1].privateKey.PublicKey, []string{}) - s.Require().NoError(err) - - response, err := s.service.Init(chats) - s.Require().NoError(err) - - actualChats := make(map[string]*Chat) - - for _, chat := range response { - actualChats[chat.ChatID] = chat - } - - s.Require().Equal(5, len(actualChats), "It creates two additional filters for the negotiated topics") - - negotiatedFilter1 := actualChats[negotiatedTopic1] - s.Require().NotNil(negotiatedFilter1, "It adds the negotiated filter") -} diff --git a/messaging/multidevice/persistence.go b/messaging/multidevice/persistence.go deleted file mode 100644 index 9de2f0b1d..000000000 --- a/messaging/multidevice/persistence.go +++ /dev/null @@ -1,16 +0,0 @@ -package multidevice - -type Persistence interface { - // GetActiveInstallations returns the active installations for a given identity. - GetActiveInstallations(maxInstallations int, identity []byte) ([]*Installation, error) - // EnableInstallation enables the installation. - EnableInstallation(identity []byte, installationID string) error - // DisableInstallation disable the installation. - DisableInstallation(identity []byte, installationID string) error - // AddInstallations adds the installations for a given identity, maintaining the enabled flag and returns the newly inserted installations - AddInstallations(identity []byte, timestamp int64, installations []*Installation, defaultEnabled bool) ([]*Installation, error) - // GetInstallations returns all the installations for a given identity - GetInstallations(identity []byte) ([]*Installation, error) - // SetInstallationMetadata sets the metadata for a given installation - SetInstallationMetadata(identity []byte, installationID string, data *InstallationMetadata) error -} diff --git a/messaging/multidevice/sql_lite_persistence_test.go b/messaging/multidevice/sql_lite_persistence_test.go deleted file mode 100644 index aa6cc5f3e..000000000 --- a/messaging/multidevice/sql_lite_persistence_test.go +++ /dev/null @@ -1,312 +0,0 @@ -package multidevice - -import ( - "database/sql" - "os" - "testing" - - msgdb "github.com/status-im/status-go/messaging/db" - "github.com/stretchr/testify/suite" -) - -const ( - dbPath = "/tmp/status-key-store.db" -) - -func TestSQLLitePersistenceTestSuite(t *testing.T) { - suite.Run(t, new(SQLLitePersistenceTestSuite)) -} - -type SQLLitePersistenceTestSuite struct { - suite.Suite - // nolint: structcheck, megacheck - db *sql.DB - service Persistence -} - -func (s *SQLLitePersistenceTestSuite) SetupTest() { - os.Remove(dbPath) - - db, err := msgdb.Open(dbPath, "", 0) - s.Require().NoError(err) - - s.service = NewSQLLitePersistence(db) -} - -func (s *SQLLitePersistenceTestSuite) TestAddInstallations() { - identity := []byte("alice") - installations := []*Installation{ - {ID: "alice-1", Version: 1, Enabled: true}, - {ID: "alice-2", Version: 2, Enabled: true}, - } - addedInstallations, err := s.service.AddInstallations( - identity, - 1, - installations, - true, - ) - s.Require().NoError(err) - - enabledInstallations, err := s.service.GetActiveInstallations(5, identity) - s.Require().NoError(err) - - s.Require().Equal(installations, enabledInstallations) - s.Require().Equal(installations, addedInstallations) -} - -func (s *SQLLitePersistenceTestSuite) TestAddInstallationVersions() { - identity := []byte("alice") - installations := []*Installation{ - {ID: "alice-1", Version: 1, Enabled: true}, - } - _, err := s.service.AddInstallations( - identity, - 1, - installations, - true, - ) - - s.Require().NoError(err) - - enabledInstallations, err := s.service.GetActiveInstallations(5, identity) - s.Require().NoError(err) - - s.Require().Equal(installations, enabledInstallations) - - installationsWithDowngradedVersion := []*Installation{ - {ID: "alice-1", Version: 0}, - } - - _, err = s.service.AddInstallations( - identity, - 3, - installationsWithDowngradedVersion, - true, - ) - s.Require().NoError(err) - - enabledInstallations, err = s.service.GetActiveInstallations(5, identity) - s.Require().NoError(err) - s.Require().Equal(installations, enabledInstallations) -} - -func (s *SQLLitePersistenceTestSuite) TestAddInstallationsLimit() { - identity := []byte("alice") - - installations := []*Installation{ - {ID: "alice-1", Version: 1}, - {ID: "alice-2", Version: 2}, - } - - _, err := s.service.AddInstallations( - identity, - 1, - installations, - true, - ) - s.Require().NoError(err) - - installations = []*Installation{ - {ID: "alice-1", Version: 1}, - {ID: "alice-3", Version: 3}, - } - - _, err = s.service.AddInstallations( - identity, - 2, - installations, - true, - ) - s.Require().NoError(err) - - installations = []*Installation{ - {ID: "alice-2", Version: 2, Enabled: true}, - {ID: "alice-3", Version: 3, Enabled: true}, - {ID: "alice-4", Version: 4, Enabled: true}, - } - - _, err = s.service.AddInstallations( - identity, - 3, - installations, - true, - ) - s.Require().NoError(err) - - enabledInstallations, err := s.service.GetActiveInstallations(3, identity) - s.Require().NoError(err) - - s.Require().Equal(installations, enabledInstallations) -} - -func (s *SQLLitePersistenceTestSuite) TestAddInstallationsDisabled() { - identity := []byte("alice") - - installations := []*Installation{ - {ID: "alice-1", Version: 1}, - {ID: "alice-2", Version: 2}, - } - - _, err := s.service.AddInstallations( - identity, - 1, - installations, - false, - ) - s.Require().NoError(err) - - actualInstallations, err := s.service.GetActiveInstallations(3, identity) - s.Require().NoError(err) - - s.Require().Nil(actualInstallations) -} - -func (s *SQLLitePersistenceTestSuite) TestDisableInstallation() { - identity := []byte("alice") - - installations := []*Installation{ - {ID: "alice-1", Version: 1}, - {ID: "alice-2", Version: 2}, - } - - _, err := s.service.AddInstallations( - identity, - 1, - installations, - true, - ) - s.Require().NoError(err) - - err = s.service.DisableInstallation(identity, "alice-1") - s.Require().NoError(err) - - // We add the installations again - installations = []*Installation{ - {ID: "alice-1", Version: 1}, - {ID: "alice-2", Version: 2}, - } - - addedInstallations, err := s.service.AddInstallations( - identity, - 1, - installations, - true, - ) - s.Require().NoError(err) - s.Require().Equal(0, len(addedInstallations)) - - actualInstallations, err := s.service.GetActiveInstallations(3, identity) - s.Require().NoError(err) - - expected := []*Installation{{ID: "alice-2", Version: 2, Enabled: true}} - s.Require().Equal(expected, actualInstallations) -} - -func (s *SQLLitePersistenceTestSuite) TestEnableInstallation() { - identity := []byte("alice") - - installations := []*Installation{ - {ID: "alice-1", Version: 1}, - {ID: "alice-2", Version: 2}, - } - - _, err := s.service.AddInstallations( - identity, - 1, - installations, - true, - ) - s.Require().NoError(err) - - err = s.service.DisableInstallation(identity, "alice-1") - s.Require().NoError(err) - - actualInstallations, err := s.service.GetActiveInstallations(3, identity) - s.Require().NoError(err) - - expected := []*Installation{{ID: "alice-2", Version: 2, Enabled: true}} - s.Require().Equal(expected, actualInstallations) - - err = s.service.EnableInstallation(identity, "alice-1") - s.Require().NoError(err) - - actualInstallations, err = s.service.GetActiveInstallations(3, identity) - s.Require().NoError(err) - - expected = []*Installation{ - {ID: "alice-1", Version: 1, Enabled: true}, - {ID: "alice-2", Version: 2, Enabled: true}, - } - s.Require().Equal(expected, actualInstallations) -} - -func (s *SQLLitePersistenceTestSuite) TestGetInstallations() { - identity := []byte("alice") - - installations := []*Installation{ - {ID: "alice-1", Version: 1}, - {ID: "alice-2", Version: 2}, - } - - _, err := s.service.AddInstallations( - identity, - 1, - installations, - true, - ) - s.Require().NoError(err) - - err = s.service.DisableInstallation(identity, "alice-1") - s.Require().NoError(err) - - actualInstallations, err := s.service.GetInstallations(identity) - s.Require().NoError(err) - - emptyMetadata := &InstallationMetadata{} - - expected := []*Installation{ - {ID: "alice-1", Version: 1, Timestamp: 1, Enabled: false, InstallationMetadata: emptyMetadata}, - {ID: "alice-2", Version: 2, Timestamp: 1, Enabled: true, InstallationMetadata: emptyMetadata}, - } - s.Require().Equal(2, len(actualInstallations)) - s.Require().ElementsMatch(expected, actualInstallations) -} - -func (s *SQLLitePersistenceTestSuite) TestSetMetadata() { - identity := []byte("alice") - - installations := []*Installation{ - {ID: "alice-1", Version: 1}, - {ID: "alice-2", Version: 2}, - } - - _, err := s.service.AddInstallations( - identity, - 1, - installations, - true, - ) - s.Require().NoError(err) - - err = s.service.DisableInstallation(identity, "alice-1") - s.Require().NoError(err) - - emptyMetadata := &InstallationMetadata{} - setMetadata := &InstallationMetadata{ - Name: "a", - FCMToken: "b", - DeviceType: "c", - } - - err = s.service.SetInstallationMetadata(identity, "alice-2", setMetadata) - s.Require().NoError(err) - - actualInstallations, err := s.service.GetInstallations(identity) - s.Require().NoError(err) - - expected := []*Installation{ - {ID: "alice-1", Version: 1, Timestamp: 1, Enabled: false, InstallationMetadata: emptyMetadata}, - {ID: "alice-2", Version: 2, Timestamp: 1, Enabled: true, InstallationMetadata: setMetadata}, - } - s.Require().ElementsMatch(expected, actualInstallations) -} diff --git a/messaging/publisher/persistence.go b/messaging/publisher/persistence.go deleted file mode 100644 index 9da063402..000000000 --- a/messaging/publisher/persistence.go +++ /dev/null @@ -1,65 +0,0 @@ -package publisher - -import ( - "database/sql" - "fmt" - "sync" -) - -type Persistence interface { - GetLastPublished() (int64, error) - SetLastPublished(int64) error - GetLastAcked(identity []byte) (int64, error) - SetLastAcked(identity []byte, lastAcked int64) error -} - -type SQLLitePersistence struct { - db *sql.DB - lastAcked map[string]int64 - lastAckedMutex sync.Mutex -} - -func NewSQLLitePersistence(db *sql.DB) *SQLLitePersistence { - return &SQLLitePersistence{ - db: db, - lastAcked: make(map[string]int64), - lastAckedMutex: sync.Mutex{}, - } -} - -func (s *SQLLitePersistence) GetLastPublished() (int64, error) { - var lastPublished int64 - statement := "SELECT last_published FROM contact_code_config LIMIT 1" - err := s.db.QueryRow(statement).Scan(&lastPublished) - - if err != nil { - return 0, err - } - - return lastPublished, nil -} - -func (s *SQLLitePersistence) SetLastPublished(lastPublished int64) error { - statement := "UPDATE contact_code_config SET last_published = ?" - stmt, err := s.db.Prepare(statement) - if err != nil { - return err - } - defer stmt.Close() - - _, err = stmt.Exec(lastPublished) - return err -} - -func (s *SQLLitePersistence) GetLastAcked(identity []byte) (int64, error) { - s.lastAckedMutex.Lock() - defer s.lastAckedMutex.Unlock() - return s.lastAcked[fmt.Sprintf("%x", identity)], nil -} - -func (s *SQLLitePersistence) SetLastAcked(identity []byte, lastAcked int64) error { - s.lastAckedMutex.Lock() - defer s.lastAckedMutex.Unlock() - s.lastAcked[fmt.Sprintf("%x", identity)] = lastAcked - return nil -} diff --git a/messaging/publisher/persistence_test.go b/messaging/publisher/persistence_test.go deleted file mode 100644 index 3cf505adb..000000000 --- a/messaging/publisher/persistence_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package publisher - -import ( - "io/ioutil" - "path/filepath" - "testing" - - "github.com/status-im/status-go/messaging/chat" - "github.com/stretchr/testify/suite" -) - -func TestPersistenceTestSuite(t *testing.T) { - suite.Run(t, new(PersistenceTestSuite)) -} - -type PersistenceTestSuite struct { - suite.Suite - persistence Persistence -} - -func (s *PersistenceTestSuite) SetupTest() { - dir, err := ioutil.TempDir("", "publisher-persistence-test") - s.Require().NoError(err) - - p, err := chat.NewSQLLitePersistence(filepath.Join(dir, "db1.sql"), "pass") - s.Require().NoError(err) - - s.persistence = NewSQLLitePersistence(p.DB) -} - -func (s *PersistenceTestSuite) TestLastAcked() { - identity := []byte("identity") - // Nothing in the database - lastAcked1, err := s.persistence.GetLastAcked(identity) - s.Require().NoError(err) - s.Require().Equal(int64(0), lastAcked1) - - err = s.persistence.SetLastAcked(identity, 3) - s.Require().NoError(err) - - lastAcked2, err := s.persistence.GetLastAcked(identity) - s.Require().NoError(err) - s.Require().Equal(int64(3), lastAcked2) -} - -func (s *PersistenceTestSuite) TestLastPublished() { - lastPublished1, err := s.persistence.GetLastPublished() - s.Require().NoError(err) - s.Require().Equal(int64(0), lastPublished1) - - err = s.persistence.SetLastPublished(3) - s.Require().NoError(err) - - lastPublished2, err := s.persistence.GetLastPublished() - s.Require().NoError(err) - s.Require().Equal(int64(3), lastPublished2) -} diff --git a/messaging/publisher/publisher.go b/messaging/publisher/publisher.go deleted file mode 100644 index fb5862bfc..000000000 --- a/messaging/publisher/publisher.go +++ /dev/null @@ -1,518 +0,0 @@ -package publisher - -import ( - "context" - "crypto/ecdsa" - "database/sql" - "errors" - "fmt" - "time" - - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - - "github.com/status-im/status-go/messaging/chat" - "github.com/status-im/status-go/messaging/chat/protobuf" - "github.com/status-im/status-go/messaging/filter" - "github.com/status-im/status-go/messaging/multidevice" - "github.com/status-im/status-go/messaging/sharedsecret" - - "github.com/status-im/status-go/services/shhext/whisperutils" - - "github.com/golang/protobuf/proto" - whisper "github.com/status-im/whisper/whisperv6" -) - -const ( - tickerInterval = 120 - // How often we should publish a contact code in seconds - publishInterval = 21600 - // How often we should check for new messages - pollIntervalMs = 300 - // Cooldown period on acking messages when not targeting our device - deviceNotFoundAckInterval = 7200 -) - -var ( - errProtocolNotInitialized = errors.New("protocol is not initialized") - // ErrPFSNotEnabled is returned when an endpoint PFS only is called but - // PFS is disabled. - ErrPFSNotEnabled = errors.New("pfs not enabled") - errNoKeySelected = errors.New("no key selected") - // ErrNoProtocolMessage means that a message was not a protocol message, - // that is it could not be unmarshaled. - ErrNoProtocolMessage = errors.New("not a protocol message") -) - -type Publisher struct { - config Config - whisper *whisper.Whisper - online func() bool - whisperAPI *whisper.PublicWhisperAPI - protocol *chat.ProtocolService - persistence Persistence - log log.Logger - filter *filter.Service - quit chan struct{} - ticker *time.Ticker -} - -type Config struct { - PFSEnabled bool -} - -func New(w *whisper.Whisper, c Config) *Publisher { - return &Publisher{ - config: c, - whisper: w, - whisperAPI: whisper.NewPublicWhisperAPI(w), - log: log.New("package", "status-go/messaging/publisher.Publisher"), - } -} - -func (p *Publisher) Init(db *sql.DB, protocol *chat.ProtocolService, onNewMessagesHandler func([]*filter.Messages)) { - - filterService := filter.New(p.whisper, filter.NewSQLLitePersistence(db), protocol.GetSharedSecretService(), onNewMessagesHandler) - - p.persistence = NewSQLLitePersistence(db) - p.protocol = protocol - p.filter = filterService -} - -func (p *Publisher) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *protobuf.Bundle) ([]*multidevice.Installation, error) { - if p.protocol == nil { - return nil, errProtocolNotInitialized - } - - return p.protocol.ProcessPublicBundle(myIdentityKey, bundle) -} - -func (p *Publisher) GetBundle(myIdentityKey *ecdsa.PrivateKey) (*protobuf.Bundle, error) { - if p.protocol == nil { - return nil, errProtocolNotInitialized - } - - return p.protocol.GetBundle(myIdentityKey) -} - -// EnableInstallation enables an installation for multi-device sync. -func (p *Publisher) EnableInstallation(installationID string) error { - if p.protocol == nil { - return errProtocolNotInitialized - } - - privateKeyID := p.whisper.SelectedKeyPairID() - if privateKeyID == "" { - return errNoKeySelected - } - - privateKey, err := p.whisper.GetPrivateKey(privateKeyID) - if err != nil { - return err - } - - return p.protocol.EnableInstallation(&privateKey.PublicKey, installationID) -} - -// DisableInstallation disables an installation for multi-device sync. -func (p *Publisher) DisableInstallation(installationID string) error { - if p.protocol == nil { - return errProtocolNotInitialized - } - - privateKeyID := p.whisper.SelectedKeyPairID() - if privateKeyID == "" { - return errNoKeySelected - } - - privateKey, err := p.whisper.GetPrivateKey(privateKeyID) - if err != nil { - return err - } - - return p.protocol.DisableInstallation(&privateKey.PublicKey, installationID) -} - -// GetOurInstallations returns all the installations available given an identity -func (p *Publisher) GetOurInstallations() ([]*multidevice.Installation, error) { - if p.protocol == nil { - return nil, errProtocolNotInitialized - } - - privateKeyID := p.whisper.SelectedKeyPairID() - if privateKeyID == "" { - return nil, errNoKeySelected - } - - privateKey, err := p.whisper.GetPrivateKey(privateKeyID) - if err != nil { - return nil, err - } - - return p.protocol.GetOurInstallations(&privateKey.PublicKey) -} - -// SetInstallationMetadata sets the metadata for our own installation -func (p *Publisher) SetInstallationMetadata(installationID string, data *multidevice.InstallationMetadata) error { - if p.protocol == nil { - return errProtocolNotInitialized - } - - privateKeyID := p.whisper.SelectedKeyPairID() - if privateKeyID == "" { - return errNoKeySelected - } - - privateKey, err := p.whisper.GetPrivateKey(privateKeyID) - if err != nil { - return err - } - - return p.protocol.SetInstallationMetadata(&privateKey.PublicKey, installationID, data) -} - -func (p *Publisher) GetPublicBundle(identityKey *ecdsa.PublicKey) (*protobuf.Bundle, error) { - if p.protocol == nil { - return nil, errProtocolNotInitialized - } - - return p.protocol.GetPublicBundle(identityKey) -} - -func (p *Publisher) Start(online func() bool, startTicker bool) error { - if p.protocol == nil { - return errProtocolNotInitialized - } - - p.online = online - if startTicker { - p.startTicker() - } - go p.filter.Start(pollIntervalMs * time.Millisecond) - return nil -} - -func (p *Publisher) Stop() error { - if p.filter != nil { - if err := p.filter.Stop(); err != nil { - log.Error("Failed to stop filter service with error", "err", err) - } - } - - return nil -} - -func (p *Publisher) getNegotiatedChat(identity *ecdsa.PublicKey) *filter.Chat { - return p.filter.GetNegotiated(identity) -} - -func (p *Publisher) LoadFilters(chats []*filter.Chat) ([]*filter.Chat, error) { - return p.filter.Init(chats) -} - -func (p *Publisher) LoadFilter(chat *filter.Chat) ([]*filter.Chat, error) { - return p.filter.Load(chat) -} - -func (p *Publisher) RemoveFilters(chats []*filter.Chat) error { - return p.filter.Remove(chats) -} -func (p *Publisher) ProcessNegotiatedSecret(secrets []*sharedsecret.Secret) { - for _, secret := range secrets { - _, err := p.filter.ProcessNegotiatedSecret(secret) - if err != nil { - log.Error("could not process negotiated filter", "err", err) - } - } -} - -func (p *Publisher) ProcessMessage(msg *whisper.Message, msgID []byte) error { - if !p.config.PFSEnabled { - return ErrPFSNotEnabled - } - - privateKeyID := p.whisper.SelectedKeyPairID() - if privateKeyID == "" { - return errNoKeySelected - } - - privateKey, err := p.whisper.GetPrivateKey(privateKeyID) - if err != nil { - return err - } - - publicKey, err := crypto.UnmarshalPubkey(msg.Sig) - if err != nil { - return err - } - - // Unmarshal message - protocolMessage := &protobuf.ProtocolMessage{} - - if err := proto.Unmarshal(msg.Payload, protocolMessage); err != nil { - p.log.Debug("Not a protocol message", "err", err) - return ErrNoProtocolMessage - } - - response, err := p.protocol.HandleMessage(privateKey, publicKey, protocolMessage, msgID) - if err == nil { - msg.Payload = response - } else if err == chat.ErrDeviceNotFound { - if err := p.handleDeviceNotFound(privateKey, publicKey); err != nil { - p.log.Error("Failed to handle DeviceNotFound", "err", err) - } - - // Return the original error - return err - } - - return err -} - -// CreateDirectMessage creates a 1:1 chat message -func (p *Publisher) CreateDirectMessage(privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, DH bool, payload []byte) (*whisper.NewMessage, error) { - if !p.config.PFSEnabled { - return nil, ErrPFSNotEnabled - } - - var ( - msgSpec *chat.ProtocolMessageSpec - err error - ) - - if DH { - p.log.Debug("Building dh message") - msgSpec, err = p.protocol.BuildDHMessage(privateKey, publicKey, payload) - } else { - p.log.Debug("Building direct message") - msgSpec, err = p.protocol.BuildDirectMessage(privateKey, publicKey, payload) - } - if err != nil { - return nil, err - } - - whisperMessage, err := p.directMessageToWhisper(privateKey, publicKey, msgSpec) - if err != nil { - p.log.Error("sshext-service", "error building whisper message", err) - return nil, err - } - - return whisperMessage, nil -} - -func (p *Publisher) directMessageToWhisper(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, spec *chat.ProtocolMessageSpec) (*whisper.NewMessage, error) { - // marshal for sending to wire - marshaledMessage, err := proto.Marshal(spec.Message) - if err != nil { - p.log.Error("encryption-service", "error marshaling message", err) - return nil, err - } - - // We rely on the fact that a deterministic ID is created for the same keys. - sigID, err := p.whisper.AddKeyPair(myPrivateKey) - if err != nil { - p.log.Error("failed to add key pair in order to get signature ID", "err", err) - return nil, err - } - - destination := hexutil.Bytes(crypto.FromECDSAPub(theirPublicKey)) - - whisperMessage := whisperutils.DefaultWhisperMessage() - whisperMessage.Payload = marshaledMessage - whisperMessage.Sig = sigID - - if spec.SharedSecret != nil { - chat := p.getNegotiatedChat(theirPublicKey) - if chat != nil { - p.log.Debug("Sending on negotiated topic", "public-key", destination) - whisperMessage.SymKeyID = chat.SymKeyID - whisperMessage.Topic = chat.Topic - whisperMessage.PublicKey = nil - return &whisperMessage, nil - } - } else if spec.PartitionedTopic() == chat.PartitionTopicV1 { - p.log.Debug("Sending on partitioned topic", "public-key", destination) - // Create filter on demand - if _, err := p.filter.LoadPartitioned(myPrivateKey, theirPublicKey, false); err != nil { - return nil, err - } - t := filter.PublicKeyToPartitionedTopicBytes(theirPublicKey) - whisperMessage.Topic = whisper.BytesToTopic(t) - whisperMessage.PublicKey = destination - return &whisperMessage, nil - } - - p.log.Debug("Sending on old discovery topic", "public-key", destination) - whisperMessage.Topic = whisperutils.DiscoveryTopicBytes - whisperMessage.PublicKey = destination - - return &whisperMessage, nil -} - -// CreatePublicMessage sends a public chat message to the underlying transport -func (p *Publisher) CreatePublicMessage(privateKey *ecdsa.PrivateKey, chatID string, payload []byte, wrap bool) (*whisper.NewMessage, error) { - if !p.config.PFSEnabled { - return nil, ErrPFSNotEnabled - } - - filter := p.filter.GetByID(chatID) - if filter == nil { - return nil, errors.New("not subscribed to chat") - } - - sigID, err := p.whisper.AddKeyPair(privateKey) - if err != nil { - return nil, fmt.Errorf("failed to get a signature ID: %v", err) - } - - p.log.Info("signature ID", sigID) - - // Enrich with transport layer info - whisperMessage := whisperutils.DefaultWhisperMessage() - whisperMessage.Sig = sigID - whisperMessage.Topic = whisperutils.ToTopic(chatID) - whisperMessage.SymKeyID = filter.SymKeyID - - if wrap { - privateKeyID := p.whisper.SelectedKeyPairID() - if privateKeyID == "" { - return nil, errNoKeySelected - } - - privateKey, err := p.whisper.GetPrivateKey(privateKeyID) - if err != nil { - return nil, err - } - - message, err := p.protocol.BuildPublicMessage(privateKey, payload) - if err != nil { - return nil, err - } - marshaledMessage, err := proto.Marshal(message) - if err != nil { - p.log.Error("encryption-service", "error marshaling message", err) - return nil, err - } - whisperMessage.Payload = marshaledMessage - - } else { - whisperMessage.Payload = payload - } - - return &whisperMessage, nil -} - -func (p *Publisher) ConfirmMessagesProcessed(ids [][]byte) error { - return p.protocol.ConfirmMessagesProcessed(ids) -} - -func (p *Publisher) startTicker() { - p.ticker = time.NewTicker(tickerInterval * time.Second) - p.quit = make(chan struct{}) - go func() { - for { - select { - case <-p.ticker.C: - _, err := p.sendContactCode() - if err != nil { - p.log.Error("could not execute tick", "err", err) - } - case <-p.quit: - p.ticker.Stop() - return - } - } - }() -} - -func (p *Publisher) sendContactCode() (*whisper.NewMessage, error) { - p.log.Info("publishing bundle") - if !p.config.PFSEnabled { - return nil, nil - } - - if p.persistence == nil { - p.log.Info("not initialized, skipping") - return nil, nil - } - - lastPublished, err := p.persistence.GetLastPublished() - if err != nil { - p.log.Error("could not fetch config from db", "err", err) - return nil, err - } - - now := time.Now().Unix() - - if now-lastPublished < publishInterval { - p.log.Debug("nothing to do") - return nil, nil - } - - if !p.online() { - p.log.Debug("not connected") - return nil, nil - } - - privateKeyID := p.whisper.SelectedKeyPairID() - if privateKeyID == "" { - return nil, errNoKeySelected - } - - privateKey, err := p.whisper.GetPrivateKey(privateKeyID) - if err != nil { - return nil, err - } - - identity := fmt.Sprintf("%x", crypto.FromECDSAPub(&privateKey.PublicKey)) - - message, err := p.CreatePublicMessage(privateKey, filter.ContactCodeTopic(identity), nil, true) - if err != nil { - p.log.Error("could not build contact code", "identity", identity, "err", err) - return nil, err - } - - _, err = p.whisperAPI.Post(context.TODO(), *message) - if err != nil { - p.log.Error("could not publish contact code on whisper", "identity", identity, "err", err) - return nil, err - } - - err = p.persistence.SetLastPublished(now) - if err != nil { - p.log.Error("could not set last published", "err", err) - return nil, err - } - - return message, nil -} - -// handleDeviceNotFound sends an empty message to publicKey containing our bundle information -// so it's notified of our devices -func (p *Publisher) handleDeviceNotFound(privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey) error { - now := time.Now().Unix() - identity := crypto.CompressPubkey(publicKey) - - lastAcked, err := p.persistence.GetLastAcked(identity) - if err != nil { - return err - } - - if now-lastAcked < deviceNotFoundAckInterval { - p.log.Debug("already acked identity", "identity", identity, "lastAcked", lastAcked) - return nil - } - - message, err := p.CreateDirectMessage(privateKey, publicKey, true, nil) - if err != nil { - return err - } - - _, err = p.whisperAPI.Post(context.TODO(), *message) - if err != nil { - return err - } - - return p.persistence.SetLastAcked(identity, now) -} diff --git a/messaging/publisher/publisher_test.go b/messaging/publisher/publisher_test.go deleted file mode 100644 index 4f796a80a..000000000 --- a/messaging/publisher/publisher_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package publisher - -import ( - "crypto/ecdsa" - "fmt" - "io/ioutil" - "path/filepath" - "testing" - - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/crypto/ecies" - "github.com/status-im/status-go/messaging/chat" - "github.com/status-im/status-go/messaging/filter" - "github.com/status-im/status-go/messaging/multidevice" - "github.com/status-im/status-go/messaging/sharedsecret" - "github.com/status-im/status-go/services/shhext/whisperutils" - whisper "github.com/status-im/whisper/whisperv6" - "github.com/stretchr/testify/suite" -) - -func TestServiceTestSuite(t *testing.T) { - suite.Run(t, new(ServiceTestSuite)) -} - -type TestKey struct { - privateKey *ecdsa.PrivateKey - keyID string - publicKeyBytes hexutil.Bytes -} - -type ServiceTestSuite struct { - suite.Suite - alice *Publisher - bob *Publisher - aliceKey *TestKey - bobKey *TestKey -} - -func (s *ServiceTestSuite) createPublisher(installationID string) (*Publisher, *TestKey) { - dir, err := ioutil.TempDir("", "publisher-test") - s.Require().NoError(err) - - config := Config{PFSEnabled: true} - - whisper := whisper.New(nil) - err = whisper.SetMinimumPoW(0) - s.Require().NoError(err) - - publisher := New(whisper, config) - - pk, err := crypto.GenerateKey() - s.Require().NoError(err) - - keyID, err := whisper.AddKeyPair(pk) - s.Require().NoError(err) - - testKey := TestKey{ - privateKey: pk, - keyID: keyID, - publicKeyBytes: crypto.FromECDSAPub(&pk.PublicKey), - } - - persistence, err := chat.NewSQLLitePersistence(filepath.Join(dir, "db1.sql"), "pass") - s.Require().NoError(err) - - sharedSecretService := sharedsecret.NewService(persistence.GetSharedSecretStorage()) - - multideviceConfig := &multidevice.Config{ - InstallationID: installationID, - ProtocolVersion: chat.ProtocolVersion, - MaxInstallations: 3, - } - multideviceService := multidevice.New(multideviceConfig, persistence.GetMultideviceStorage()) - - protocolService := chat.NewProtocolService( - chat.NewEncryptionService( - persistence, - chat.DefaultEncryptionServiceConfig(installationID)), - sharedSecretService, - multideviceService, - func(addedBundles []*multidevice.Installation) {}, - publisher.ProcessNegotiatedSecret, - ) - - publisher.Init(persistence.DB, protocolService, func(msg []*filter.Messages) {}) - - err = publisher.Start(func() bool { return true }, false) - s.Require().NoError(err) - - return publisher, &testKey -} - -func (s *ServiceTestSuite) SetupTest() { - s.alice, s.aliceKey = s.createPublisher("installation-1") - _, err := s.alice.LoadFilters([]*filter.Chat{}) - s.Require().NoError(err) - - s.bob, s.bobKey = s.createPublisher("installation-2") - _, err = s.bob.LoadFilters([]*filter.Chat{}) - s.Require().NoError(err) -} - -func (s *ServiceTestSuite) TestCreateDirectMessage() { - newMessage, err := s.alice.CreateDirectMessage(s.aliceKey.privateKey, &s.bobKey.privateKey.PublicKey, false, []byte("hello")) - s.Require().NoError(err) - - message := &whisper.Message{ - Sig: s.aliceKey.publicKeyBytes, - Topic: newMessage.Topic, - Payload: newMessage.Payload, - Dst: newMessage.PublicKey, - } - - err = s.bob.ProcessMessage(message, []byte("1")) - s.Require().NoError(err) - s.Require().Equal([]byte("hello"), message.Payload) -} - -func (s *ServiceTestSuite) TestTopic() { - // We build an initial message - newMessage1, err := s.alice.CreateDirectMessage(s.aliceKey.privateKey, &s.bobKey.privateKey.PublicKey, false, []byte("hello")) - s.Require().NoError(err) - - message1 := &whisper.Message{ - Sig: s.aliceKey.publicKeyBytes, - Topic: newMessage1.Topic, - Payload: newMessage1.Payload, - Dst: newMessage1.PublicKey, - } - - // We have no information, it should use the discovery topic - s.Require().Equal(whisperutils.DiscoveryTopicBytes, message1.Topic) - - // We build a contact code from user 2 - newMessage2, err := s.bob.sendContactCode() - s.Require().NoError(err) - s.Require().NotNil(newMessage2) - - message2 := &whisper.Message{ - Sig: s.bobKey.publicKeyBytes, - Topic: newMessage2.Topic, - Payload: newMessage2.Payload, - Dst: newMessage2.PublicKey, - } - - // We receive the contact code - err = s.alice.ProcessMessage(message2, []byte("1")) - s.Require().EqualError(err, chat.ErrNoPayload.Error()) - - // We build another message, this time it should use the partitioned topic - newMessage3, err := s.alice.CreateDirectMessage(s.aliceKey.privateKey, &s.bobKey.privateKey.PublicKey, false, []byte("hello")) - s.Require().NoError(err) - - message3 := &whisper.Message{ - Sig: s.aliceKey.publicKeyBytes, - Topic: newMessage3.Topic, - Payload: newMessage3.Payload, - Dst: newMessage3.PublicKey, - } - expectedTopic3 := whisper.BytesToTopic(filter.PublicKeyToPartitionedTopicBytes(&s.bobKey.privateKey.PublicKey)) - - s.Require().Equal(expectedTopic3, message3.Topic) - - // We receive the message - err = s.bob.ProcessMessage(message3, []byte("1")) - s.Require().NoError(err) - - // We build another message, this time it should use the negotiated topic - newMessage4, err := s.bob.CreateDirectMessage(s.bobKey.privateKey, &s.aliceKey.privateKey.PublicKey, false, []byte("hello")) - s.Require().NoError(err) - - message4 := &whisper.Message{ - Sig: s.bobKey.publicKeyBytes, - Topic: newMessage4.Topic, - Payload: newMessage4.Payload, - Dst: newMessage4.PublicKey, - } - sharedSecret, err := ecies.ImportECDSA(s.bobKey.privateKey).GenerateShared( - ecies.ImportECDSAPublic(&s.aliceKey.privateKey.PublicKey), - 16, - 16) - s.Require().NoError(err) - keyString := fmt.Sprintf("%x", sharedSecret) - - negotiatedTopic := whisper.BytesToTopic(filter.ToTopic(keyString)) - - s.Require().Equal(negotiatedTopic, message4.Topic) - - // We receive the message - err = s.alice.ProcessMessage(message4, []byte("1")) - s.Require().NoError(err) - - // Alice sends another message to Bob, this time it should use the negotiated topic - newMessage5, err := s.alice.CreateDirectMessage(s.aliceKey.privateKey, &s.bobKey.privateKey.PublicKey, false, []byte("hello")) - s.Require().NoError(err) - - message5 := &whisper.Message{ - Sig: s.aliceKey.publicKeyBytes, - Topic: newMessage5.Topic, - Payload: newMessage5.Payload, - Dst: newMessage5.PublicKey, - } - s.Require().NoError(err) - s.Require().Equal(negotiatedTopic, message5.Topic) - -} diff --git a/messaging/sharedsecret/service.go b/messaging/sharedsecret/service.go deleted file mode 100644 index bfd07354a..000000000 --- a/messaging/sharedsecret/service.go +++ /dev/null @@ -1,104 +0,0 @@ -package sharedsecret - -import ( - "crypto/ecdsa" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/crypto/ecies" - "github.com/ethereum/go-ethereum/log" -) - -const sskLen = 16 - -type Service struct { - log log.Logger - persistence Persistence -} - -func NewService(persistence Persistence) *Service { - return &Service{ - log: log.New("package", "status-go/messaging/sharedsecret.Service"), - persistence: persistence, - } -} - -func (s *Service) setup(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, installationID string) (*Secret, error) { - s.log.Debug("Setup called for", "installationID", installationID) - sharedKey, err := ecies.ImportECDSA(myPrivateKey).GenerateShared( - ecies.ImportECDSAPublic(theirPublicKey), - sskLen, - sskLen, - ) - if err != nil { - return nil, err - } - - theirIdentity := crypto.CompressPubkey(theirPublicKey) - if err = s.persistence.Add(theirIdentity, sharedKey, installationID); err != nil { - return nil, err - } - - return &Secret{Key: sharedKey, Identity: theirPublicKey}, err -} - -// Receive will generate a shared secret for a given identity, and return it -func (s *Service) Receive(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, installationID string) (*Secret, error) { - s.log.Debug("Received message, setting up topic", "public-key", theirPublicKey, "installation-id", installationID) - return s.setup(myPrivateKey, theirPublicKey, installationID) -} - -// Send returns a shared key and whether it has been acknowledged from all the installationIDs -func (s *Service) Send(myPrivateKey *ecdsa.PrivateKey, myInstallationID string, theirPublicKey *ecdsa.PublicKey, theirInstallationIDs []string) (*Secret, bool, error) { - s.log.Debug("Checking against:", "installation-ids", theirInstallationIDs) - secret, err := s.setup(myPrivateKey, theirPublicKey, myInstallationID) - if err != nil { - return nil, false, err - } - - if len(theirInstallationIDs) == 0 { - return secret, false, nil - } - - theirIdentity := crypto.CompressPubkey(theirPublicKey) - response, err := s.persistence.Get(theirIdentity, theirInstallationIDs) - if err != nil { - return nil, false, err - } - - for _, installationID := range theirInstallationIDs { - if !response.installationIDs[installationID] { - s.log.Debug("no shared secret with:", "installation-id", installationID) - return secret, false, nil - } - } - - s.log.Debug("shared secret found") - - return secret, true, nil -} - -type Secret struct { - Identity *ecdsa.PublicKey - Key []byte -} - -func (s *Service) All() ([]*Secret, error) { - var secrets []*Secret - tuples, err := s.persistence.All() - if err != nil { - return nil, err - } - - for _, tuple := range tuples { - key, err := crypto.DecompressPubkey(tuple[0]) - if err != nil { - return nil, err - } - - secrets = append(secrets, &Secret{Identity: key, Key: tuple[1]}) - - } - - return secrets, nil - -} diff --git a/messaging/sharedsecret/service_test.go b/messaging/sharedsecret/service_test.go deleted file mode 100644 index 7693260c2..000000000 --- a/messaging/sharedsecret/service_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package sharedsecret - -import ( - "io/ioutil" - "os" - "testing" - - "github.com/ethereum/go-ethereum/crypto" - msgdb "github.com/status-im/status-go/messaging/db" - "github.com/stretchr/testify/suite" -) - -func TestServiceTestSuite(t *testing.T) { - suite.Run(t, new(ServiceTestSuite)) -} - -type ServiceTestSuite struct { - suite.Suite - service *Service - path string -} - -func (s *ServiceTestSuite) SetupTest() { - dbFile, err := ioutil.TempFile(os.TempDir(), "sharedsecret") - s.Require().NoError(err) - s.path = dbFile.Name() - - db, err := msgdb.Open(s.path, "", 0) - - s.Require().NoError(err) - - s.service = NewService(NewSQLLitePersistence(db)) -} - -func (s *ServiceTestSuite) TearDownTest() { - os.Remove(s.path) -} - -func (s *ServiceTestSuite) TestSingleInstallationID() { - ourInstallationID := "our" - installationID1 := "1" - installationID2 := "2" - - myKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - theirKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - // We receive a message from installationID1 - sharedKey1, err := s.service.Receive(myKey, &theirKey.PublicKey, installationID1) - s.Require().NoError(err) - s.Require().NotNil(sharedKey1, "it generates a shared key") - - // We want to send a message to installationID1 - sharedKey2, agreed2, err := s.service.Send(myKey, ourInstallationID, &theirKey.PublicKey, []string{installationID1}) - s.Require().NoError(err) - s.Require().True(agreed2) - s.Require().NotNil(sharedKey2, "We can retrieve a shared secret") - s.Require().Equal(sharedKey1, sharedKey2, "The shared secret is the same as the one stored") - - // We want to send a message to multiple installationIDs, one of which we haven't never communicated with - sharedKey3, agreed3, err := s.service.Send(myKey, ourInstallationID, &theirKey.PublicKey, []string{installationID1, installationID2}) - s.Require().NoError(err) - s.Require().NotNil(sharedKey3, "A shared key is returned") - s.Require().False(agreed3) - - // We receive a message from installationID2 - sharedKey4, err := s.service.Receive(myKey, &theirKey.PublicKey, installationID2) - s.Require().NoError(err) - s.Require().NotNil(sharedKey4, "it generates a shared key") - s.Require().Equal(sharedKey1, sharedKey4, "It generates the same key") - - // We want to send a message to installationID 1 & 2, both have been - sharedKey5, agreed5, err := s.service.Send(myKey, ourInstallationID, &theirKey.PublicKey, []string{installationID1, installationID2}) - s.Require().NoError(err) - s.Require().NotNil(sharedKey5, "We can retrieve a shared secret") - s.Require().True(agreed5) - s.Require().Equal(sharedKey1, sharedKey5, "The shared secret is the same as the one stored") - -} - -func (s *ServiceTestSuite) TestAll() { - installationID1 := "1" - installationID2 := "2" - - myKey, err := crypto.GenerateKey() - s.Require().NoError(err) - - theirKey1, err := crypto.GenerateKey() - s.Require().NoError(err) - - theirKey2, err := crypto.GenerateKey() - s.Require().NoError(err) - - // We receive a message from user 1 - sharedKey1, err := s.service.Receive(myKey, &theirKey1.PublicKey, installationID1) - s.Require().NoError(err) - s.Require().NotNil(sharedKey1, "it generates a shared key") - - // We receive a message from user 2 - sharedKey2, err := s.service.Receive(myKey, &theirKey2.PublicKey, installationID2) - s.Require().NoError(err) - s.Require().NotNil(sharedKey2, "it generates a shared key") - - // All the secrets are there - secrets, err := s.service.All() - s.Require().NoError(err) - expected := []*Secret{ - sharedKey1, - sharedKey2, - } - s.Require().Equal(expected, secrets) -} diff --git a/services/shhext/api.go b/services/shhext/api.go index 3d7a6b022..0b5125b39 100644 --- a/services/shhext/api.go +++ b/services/shhext/api.go @@ -9,20 +9,21 @@ import ( "math/big" "time" + "github.com/status-im/status-go/services/shhext/dedup" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rlp" "github.com/status-im/status-go/db" "github.com/status-im/status-go/mailserver" - "github.com/status-im/status-go/messaging/filter" - "github.com/status-im/status-go/messaging/multidevice" - "github.com/status-im/status-go/services/shhext/dedup" "github.com/status-im/status-go/services/shhext/mailservers" whisper "github.com/status-im/whisper/whisperv6" + + "github.com/status-im/status-protocol-go/encryption/multidevice" + "github.com/status-im/status-protocol-go/transport/whisper/filter" ) const ( @@ -188,18 +189,6 @@ func NewPublicAPI(s *Service) *PublicAPI { } } -// Post shamelessly copied from whisper codebase with slight modifications. -func (api *PublicAPI) Post(ctx context.Context, req whisper.NewMessage) (hexutil.Bytes, error) { - hexID, err := api.publicAPI.Post(ctx, req) - if err == nil { - api.service.envelopesMonitor.Add(common.BytesToHash(hexID), req) - } else { - return nil, err - } - mID := messageID(req) - return mID[:], err -} - func (api *PublicAPI) getPeer(rawurl string) (*enode.Node, error) { if len(rawurl) == 0 { return mailservers.GetFirstConnected(api.service.server, api.service.peerStore) @@ -409,14 +398,13 @@ func (api *PublicAPI) SyncMessages(ctx context.Context, r SyncMessagesRequest) ( } } -// GetNewFilterMessages is a prototype method with deduplication +// GetNewFilterMessages is a prototype method with deduplication. func (api *PublicAPI) GetNewFilterMessages(filterID string) ([]dedup.DeduplicateMessage, error) { - msgs, err := api.publicAPI.GetFilterMessages(filterID) + messages, err := api.publicAPI.GetFilterMessages(filterID) if err != nil { return nil, err } - - return api.service.processReceivedMessages(msgs) + return api.service.deduplicator.Deduplicate(messages), nil } // ConfirmMessagesProcessed is a method to confirm that messages was consumed by @@ -443,47 +431,49 @@ func (api *PublicAPI) ConfirmMessagesProcessed(messages []*whisper.Message) (err // ConfirmMessagesProcessedByID is a method to confirm that messages was consumed by // the client side. +// TODO: this is broken now as it requires dedup ID while a message hash should be used. func (api *PublicAPI) ConfirmMessagesProcessedByID(messageIDs [][]byte) error { - if err := api.service.ConfirmMessagesProcessed(messageIDs); err != nil { + /*if err := api.service.ConfirmMessagesProcessed(messageIDs); err != nil { return err - } + }*/ return api.service.deduplicator.AddMessageByID(messageIDs) } -// SendPublicMessage sends a public chat message to the underlying transport -func (api *PublicAPI) SendPublicMessage(ctx context.Context, msg SendPublicMessageRPC) (hexutil.Bytes, error) { - privateKey, err := api.service.w.GetPrivateKey(msg.Sig) - if err != nil { - return nil, fmt.Errorf("failed to obtain a private key from Sig: %v", err) - } - - message, err := api.service.CreatePublicMessage(privateKey, msg.Chat, msg.Payload, false) +// Post is used to send one-to-one for those who did not enabled device-to-device sync, +// in other words don't use PFS-enabled messages. Otherwise, SendDirectMessage is used. +// It's important to call PublicAPI.afterSend() so that the client receives a signal +// with confirmation that the message left the device. +func (api *PublicAPI) Post(ctx context.Context, newMessage whisper.NewMessage) (hexutil.Bytes, error) { + hash, err := api.publicAPI.Post(ctx, newMessage) if err != nil { return nil, err } + return api.service.afterPost(hash, newMessage), nil +} - return api.Post(ctx, *message) +// SendPublicMessage sends a public chat message to the underlying transport. +// Message's payload is a transit encoded message. +// It's important to call PublicAPI.afterSend() so that the client receives a signal +// with confirmation that the message left the device. +func (api *PublicAPI) SendPublicMessage(ctx context.Context, msg SendPublicMessageRPC) (hexutil.Bytes, error) { + hash, newMessage, err := api.service.messenger.SendRaw(ctx, msg, msg.Payload) + if err != nil { + return nil, err + } + return api.service.afterPost(hash, newMessage), nil } // SendDirectMessage sends a 1:1 chat message to the underlying transport +// Message's payload is a transit encoded message. +// It's important to call PublicAPI.afterSend() so that the client receives a signal +// with confirmation that the message left the device. func (api *PublicAPI) SendDirectMessage(ctx context.Context, msg SendDirectMessageRPC) (hexutil.Bytes, error) { - privateKey, err := api.service.w.GetPrivateKey(msg.Sig) + hash, newMessage, err := api.service.messenger.SendRaw(ctx, msg, msg.Payload) if err != nil { return nil, err } - - publicKey, err := crypto.UnmarshalPubkey(msg.PubKey) - if err != nil { - return nil, err - } - - message, err := api.service.CreateDirectMessage(privateKey, publicKey, msg.DH, msg.Payload) - if err != nil { - return nil, err - } - - return api.Post(ctx, *message) + return api.service.afterPost(hash, newMessage), nil } func (api *PublicAPI) requestMessagesUsingPayload(request db.HistoryRequest, peer, symkeyID string, payload []byte, force bool, timeout time.Duration, topics []whisper.TopicType) (hash common.Hash, err error) { @@ -594,39 +584,32 @@ func (api *PublicAPI) CompleteRequest(parent context.Context, hex string) (err e return err } -// LoadFilters load all the necessary filters func (api *PublicAPI) LoadFilters(parent context.Context, chats []*filter.Chat) ([]*filter.Chat, error) { - return api.service.LoadFilters(chats) + return api.service.messenger.LoadFilters(chats) } -// LoadFilter load a single filter -func (api *PublicAPI) LoadFilter(parent context.Context, chat *filter.Chat) ([]*filter.Chat, error) { - return api.service.LoadFilter(chat) -} - -// RemoveFilter remove a single filter func (api *PublicAPI) RemoveFilters(parent context.Context, chats []*filter.Chat) error { - return api.service.RemoveFilters(chats) + return api.service.messenger.RemoveFilters(chats) } // EnableInstallation enables an installation for multi-device sync. func (api *PublicAPI) EnableInstallation(installationID string) error { - return api.service.EnableInstallation(installationID) + return api.service.messenger.EnableInstallation(installationID) } // DisableInstallation disables an installation for multi-device sync. func (api *PublicAPI) DisableInstallation(installationID string) error { - return api.service.DisableInstallation(installationID) + return api.service.messenger.DisableInstallation(installationID) } // GetOurInstallations returns all the installations available given an identity func (api *PublicAPI) GetOurInstallations() ([]*multidevice.Installation, error) { - return api.service.GetOurInstallations() + return api.service.messenger.Installations() } // SetInstallationMetadata sets the metadata for our own installation func (api *PublicAPI) SetInstallationMetadata(installationID string, data *multidevice.InstallationMetadata) error { - return api.service.SetInstallationMetadata(installationID, data) + return api.service.messenger.SetInstallationMetadata(installationID, data) } // ----- diff --git a/services/shhext/api_test.go b/services/shhext/api_test.go index bffa9deec..89b277808 100644 --- a/services/shhext/api_test.go +++ b/services/shhext/api_test.go @@ -4,12 +4,16 @@ import ( "context" "encoding/hex" "fmt" + "github.com/ethereum/go-ethereum/crypto" + "github.com/status-im/status-go/params" + "io/ioutil" "testing" "time" "github.com/ethereum/go-ethereum/common" "github.com/status-im/status-go/mailserver" + protocol "github.com/status-im/status-protocol-go" whisper "github.com/status-im/whisper/whisperv6" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -237,3 +241,56 @@ func TestExpiredOrCompleted(t *testing.T) { require.EqualError(t, err, fmt.Sprintf("request %x expired", hash)) } } + +func TestAfterPostIsCalled(t *testing.T) { + shhConfig := whisper.DefaultConfig + shhConfig.MinimumAcceptedPOW = 0 + w := whisper.New(&shhConfig) + symKeyID, err := w.AddSymKeyFromPassword("abc") + require.NoError(t, err) + + identity, err := crypto.GenerateKey() + require.NoError(t, err) + + tmpDir, err := ioutil.TempDir("", "test-after-post") + require.NoError(t, err) + + service := New(w, nil, nil, params.ShhextConfig{}) + service.messenger, err = protocol.NewMessenger( + identity, + nil, + w, + tmpDir, + "encKey", + "installation1", + ) + require.NoError(t, err) + + api := NewPublicAPI(service) + + // Using api.Post() + hash1, err := api.Post(context.Background(), whisper.NewMessage{ + SymKeyID: symKeyID, + Topic: stringToTopic("topic"), + }) + require.NoError(t, err) + require.Equal(t, EnvelopePosted, service.envelopesMonitor.GetMessageState(common.BytesToHash(hash1))) + + // Using api.SendPublicMessage() + hash2, err := api.SendPublicMessage(context.Background(), SendPublicMessageRPC{ + Chat: "test-channel", + Payload: []byte("abc"), + }) + require.NoError(t, err) + require.Equal(t, EnvelopePosted, service.envelopesMonitor.GetMessageState(common.BytesToHash(hash2))) + + // Using api.SendDirectMessage() + recipient, err := crypto.GenerateKey() + require.NoError(t, err) + hash3, err := api.SendDirectMessage(context.Background(), SendDirectMessageRPC{ + PubKey: crypto.FromECDSAPub(&recipient.PublicKey), + Payload: []byte("abc"), + }) + require.NoError(t, err) + require.Equal(t, EnvelopePosted, service.envelopesMonitor.GetMessageState(common.BytesToHash(hash3))) +} diff --git a/messaging/db/db.go b/services/shhext/db.go similarity index 57% rename from messaging/db/db.go rename to services/shhext/db.go index 3e8b47033..8e6c0876d 100644 --- a/messaging/db/db.go +++ b/services/shhext/db.go @@ -1,15 +1,12 @@ -package db +package shhext import ( - "database/sql" "fmt" "os" - sqlite "github.com/mutecomm/go-sqlcipher" // We require go sqlcipher that overrides default implementation - "github.com/status-im/migrate/v4" - "github.com/status-im/migrate/v4/database/sqlcipher" - "github.com/status-im/migrate/v4/source/go_bindata" - "github.com/status-im/status-go/messaging/db/migrations" + sqliteDriver "github.com/mutecomm/go-sqlcipher" // We require go sqlcipher that overrides default implementation + + "github.com/status-im/status-protocol-go/sqlite" ) const exportDB = "SELECT sqlcipher_export('newdb')" @@ -25,7 +22,7 @@ const defaultKdfIterationsNumber = 64000 // https://notes.status.im/i8Y_l7ccTiOYq09HVgoFwA const KdfIterationsNumber = 3200 -func MigrateDBFile(oldPath string, newPath string, oldKey string, newKey string) error { +func migrateDBFile(oldPath string, newPath string, oldKey string, newKey string) error { _, err := os.Stat(oldPath) // No files, nothing to do @@ -42,7 +39,7 @@ func MigrateDBFile(oldPath string, newPath string, oldKey string, newKey string) return err } - db, err := Open(newPath, oldKey, defaultKdfIterationsNumber) + db, err := sqlite.OpenWithIter(newPath, oldKey, defaultKdfIterationsNumber, sqlite.MigrationConfig{}) if err != nil { return err } @@ -54,16 +51,15 @@ func MigrateDBFile(oldPath string, newPath string, oldKey string, newKey string) } return nil - } -// MigrateDBKeyKdfIterations changes the number of kdf iterations executed +// migrateDBKeyKdfIterations changes the number of kdf iterations executed // during the database key derivation. This change is necessary because // of performance reasons. // https://github.com/status-im/status-go/pull/1343 // `sqlcipher_export` is used for migration, check out this link for details: // https://www.zetetic.net/sqlcipher/sqlcipher-api/#sqlcipher_export -func MigrateDBKeyKdfIterations(oldPath string, newPath string, key string) error { +func migrateDBKeyKdfIterations(oldPath string, newPath string, key string) error { _, err := os.Stat(oldPath) // No files, nothing to do @@ -76,7 +72,7 @@ func MigrateDBKeyKdfIterations(oldPath string, newPath string, key string) error return err } - isEncrypted, err := sqlite.IsEncrypted(oldPath) + isEncrypted, err := sqliteDriver.IsEncrypted(oldPath) if err != nil { return err } @@ -86,7 +82,7 @@ func MigrateDBKeyKdfIterations(oldPath string, newPath string, key string) error return os.Rename(oldPath, newPath) } - db, err := Open(oldPath, key, defaultKdfIterationsNumber) + db, err := sqlite.OpenWithIter(oldPath, key, defaultKdfIterationsNumber, sqlite.MigrationConfig{}) if err != nil { return err } @@ -119,8 +115,8 @@ func MigrateDBKeyKdfIterations(oldPath string, newPath string, key string) error return os.Remove(oldPath) } -// EncryptDatabase encrypts an unencrypted database with key -func EncryptDatabase(oldPath string, newPath string, key string) error { +// encryptDatabase encrypts an unencrypted database with key +func encryptDatabase(oldPath string, newPath string, key string) error { _, err := os.Stat(oldPath) // No files, nothing to do @@ -133,7 +129,7 @@ func EncryptDatabase(oldPath string, newPath string, key string) error { return err } - isEncrypted, err := sqlite.IsEncrypted(oldPath) + isEncrypted, err := sqliteDriver.IsEncrypted(oldPath) if err != nil { return err } @@ -143,7 +139,7 @@ func EncryptDatabase(oldPath string, newPath string, key string) error { return os.Rename(oldPath, newPath) } - db, err := Open(oldPath, "", defaultKdfIterationsNumber) + db, err := sqlite.OpenWithIter(oldPath, "", defaultKdfIterationsNumber, sqlite.MigrationConfig{}) if err != nil { return err } @@ -175,70 +171,3 @@ func EncryptDatabase(oldPath string, newPath string, key string) error { return os.Remove(oldPath) } - -func migrateDB(db *sql.DB) error { - resources := bindata.Resource( - migrations.AssetNames(), - func(name string) ([]byte, error) { - return migrations.Asset(name) - }, - ) - - source, err := bindata.WithInstance(resources) - if err != nil { - return err - } - - driver, err := sqlcipher.WithInstance(db, &sqlcipher.Config{}) - if err != nil { - return err - } - - m, err := migrate.NewWithInstance( - "go-bindata", - source, - "sqlcipher", - driver) - if err != nil { - return err - } - - if err = m.Up(); err != migrate.ErrNoChange { - return err - } - - return nil -} - -func Open(path string, key string, kdfIter int) (*sql.DB, error) { - db, err := sql.Open("sqlite3", path) - if err != nil { - return nil, err - } - - keyString := fmt.Sprintf("PRAGMA key = '%s'", key) - - // Disable concurrent access as not supported by the driver - db.SetMaxOpenConns(1) - - if _, err = db.Exec("PRAGMA foreign_keys=ON"); err != nil { - return nil, err - } - - if _, err = db.Exec(keyString); err != nil { - return nil, err - } - - kdfString := fmt.Sprintf("PRAGMA kdf_iter = '%d'", kdfIter) - - if _, err = db.Exec(kdfString); err != nil { - return nil, err - } - - // Migrate db - if err = migrateDB(db); err != nil { - return nil, err - } - - return db, nil -} diff --git a/services/shhext/file.go b/services/shhext/file.go new file mode 100644 index 000000000..0e58f9acd --- /dev/null +++ b/services/shhext/file.go @@ -0,0 +1,49 @@ +package shhext + +import ( + "io" + "io/ioutil" + "os" + "path/filepath" +) + +// copyFile implementation is borrowed from https://go-review.googlesource.com/c/go/+/1591 +// which didn't make into ioutil package. +// A slight modification is that the file permissions are copied from the source file. +// Another modification is the order of parameters which was reversed. +func copyFile(src, dst string) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + + inPerm, err := in.Stat() + if err != nil { + return err + } + + tmp, err := ioutil.TempFile(filepath.Dir(dst), "") + if err != nil { + return err + } + + _, err = io.Copy(tmp, in) + if err != nil { + tmp.Close() + os.Remove(tmp.Name()) + return err + } + + if err = tmp.Close(); err != nil { + os.Remove(tmp.Name()) + return err + } + + if err = os.Chmod(tmp.Name(), inPerm.Mode()); err != nil { + os.Remove(tmp.Name()) + return err + } + + return os.Rename(tmp.Name(), dst) +} diff --git a/services/shhext/rpc.go b/services/shhext/rpc.go index 152559368..2aabf41e3 100644 --- a/services/shhext/rpc.go +++ b/services/shhext/rpc.go @@ -4,21 +4,63 @@ package shhext import ( + "crypto/ecdsa" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" ) // SendPublicMessageRPC represents the RPC payload for the SendPublicMessage RPC method type SendPublicMessageRPC struct { - Sig string + Sig string // TODO: remove Chat string Payload hexutil.Bytes } +// TODO: implement with accordance to https://github.com/status-im/status-protocol-go/issues/28. +func (m SendPublicMessageRPC) ID() string { return m.Chat } + +func (m SendPublicMessageRPC) PublicName() string { return m.Chat } + +func (m SendPublicMessageRPC) PublicKey() *ecdsa.PublicKey { return nil } + // SendDirectMessageRPC represents the RPC payload for the SendDirectMessage RPC method type SendDirectMessageRPC struct { - Sig string + Sig string // TODO: remove Chat string Payload hexutil.Bytes PubKey hexutil.Bytes - DH bool + DH bool // TODO: make sure to remove safely +} + +// TODO: implement with accordance to https://github.com/status-im/status-protocol-go/issues/28. +func (m SendDirectMessageRPC) ID() string { return "" } + +func (m SendDirectMessageRPC) PublicName() string { return "" } + +func (m SendDirectMessageRPC) PublicKey() *ecdsa.PublicKey { + publicKey, _ := crypto.UnmarshalPubkey(m.PubKey) + return publicKey +} + +type JoinRPC struct { + Chat string + PubKey hexutil.Bytes + Payload hexutil.Bytes +} + +func (m JoinRPC) ID() string { return m.Chat } + +func (m JoinRPC) PublicName() string { + if len(m.PubKey) > 0 { + return "" + } + return m.Chat +} + +func (m JoinRPC) PublicKey() *ecdsa.PublicKey { + if len(m.PubKey) > 0 { + return nil + } + publicKey, _ := crypto.UnmarshalPubkey(m.PubKey) + return publicKey } diff --git a/services/shhext/server.go b/services/shhext/server.go new file mode 100644 index 000000000..b9c80b181 --- /dev/null +++ b/services/shhext/server.go @@ -0,0 +1,38 @@ +package shhext + +import ( + "crypto/ecdsa" + + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +type server struct { + server *p2p.Server +} + +func (s *server) NodeID() *ecdsa.PrivateKey { + return s.server.PrivateKey +} + +func (s *server) Online() bool { + return s.server.PeerCount() != 0 +} + +func (s *server) AddPeer(url string) error { + parsedNode, err := enode.ParseV4(url) + if err != nil { + return err + } + s.server.AddPeer(parsedNode) + return nil +} + +func (s *server) Connected(id enode.ID) (bool, error) { + for _, p := range s.server.Peers() { + if p.ID() == id { + return true, nil + } + } + return false, nil +} diff --git a/services/shhext/service.go b/services/shhext/service.go index d9e7c21cd..a1cfe2052 100644 --- a/services/shhext/service.go +++ b/services/shhext/service.go @@ -4,6 +4,8 @@ import ( "context" "crypto/ecdsa" "fmt" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/status-im/status-go/logutils" "os" "path/filepath" "time" @@ -16,17 +18,12 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/status-im/status-go/db" - "github.com/status-im/status-go/messaging/chat" - msgdb "github.com/status-im/status-go/messaging/db" - "github.com/status-im/status-go/messaging/filter" - "github.com/status-im/status-go/messaging/multidevice" - "github.com/status-im/status-go/messaging/publisher" - "github.com/status-im/status-go/messaging/sharedsecret" "github.com/status-im/status-go/params" "github.com/status-im/status-go/services/shhext/dedup" "github.com/status-im/status-go/services/shhext/mailservers" "github.com/status-im/status-go/signal" + protocol "github.com/status-im/status-protocol-go" whisper "github.com/status-im/whisper/whisperv6" "github.com/syndtr/goleveldb/leveldb" "golang.org/x/crypto/sha3" @@ -37,8 +34,6 @@ const ( defaultConnectionsTarget = 1 // defaultTimeoutWaitAdded is a timeout to use to establish initial connections. defaultTimeoutWaitAdded = 5 * time.Second - // maxInstallations is a maximum number of supported devices for one account. - maxInstallations = 3 ) // EnvelopeEventsHandler used for two different event types. @@ -51,7 +46,9 @@ type EnvelopeEventsHandler interface { // Service is a service that provides some additional Whisper API. type Service struct { - *publisher.Publisher + messenger *protocol.Messenger + cancelMessenger chan struct{} + storage db.TransactionalStorage w *whisper.Whisper config params.ShhextConfig @@ -66,7 +63,6 @@ type Service struct { cache *mailservers.Cache connManager *mailservers.ConnectionManager lastUsedMonitor *mailservers.LastUsedConnectionMonitor - filter *filter.Service } // Make sure that Service implements node.Service interface. @@ -89,9 +85,7 @@ func New(w *whisper.Whisper, handler EnvelopeEventsHandler, ldb *leveldb.DB, con requestsRegistry: requestsRegistry, } envelopesMonitor := NewEnvelopesMonitor(w, handler, config.MailServerConfirmations, ps, config.MaxMessageDeliveryAttempts) - publisher := publisher.New(w, publisher.Config{PFSEnabled: config.PFSEnabled}) return &Service{ - Publisher: publisher, storage: db.NewLevelDBStorage(ldb), w: w, config: config, @@ -133,11 +127,11 @@ func (s *Service) initProtocol(address, encKey, password string) error { v4Path := filepath.Join(dataDir, fmt.Sprintf("%s.v4.db", s.config.InstallationID)) if password != "" { - if err := msgdb.MigrateDBFile(v0Path, v1Path, "ON", password); err != nil { + if err := migrateDBFile(v0Path, v1Path, "ON", password); err != nil { return err } - if err := msgdb.MigrateDBFile(v1Path, v2Path, password, encKey); err != nil { + if err := migrateDBFile(v1Path, v2Path, password, encKey); err != nil { // Remove db file as created with a blank password and never used, // and there's no need to rekey in this case os.Remove(v1Path) @@ -145,13 +139,13 @@ func (s *Service) initProtocol(address, encKey, password string) error { } } - if err := msgdb.MigrateDBKeyKdfIterations(v2Path, v3Path, encKey); err != nil { + if err := migrateDBKeyKdfIterations(v2Path, v3Path, encKey); err != nil { os.Remove(v2Path) os.Remove(v3Path) } // Fix IOS not encrypting database - if err := msgdb.EncryptDatabase(v3Path, v4Path, encKey); err != nil { + if err := encryptDatabase(v3Path, v4Path, encKey); err != nil { os.Remove(v3Path) os.Remove(v4Path) } @@ -168,74 +162,103 @@ func (s *Service) initProtocol(address, encKey, password string) error { return err } - persistence, err := chat.NewSQLLitePersistence(v4Path, encKey) + // Because status-protocol-go split a single database file into multiple ones, + // in order to keep the backward compatibility, we just copy existing database + // into multiple locations. The tables and schemas did not change so it will work. + sessionsDatabasePath := filepath.Join(dataDir, fmt.Sprintf("%s.sessions.v4.sql", s.config.InstallationID)) + transportDatabasePath := filepath.Join(dataDir, fmt.Sprintf("%s.transport.v4.sql", s.config.InstallationID)) + if _, err := os.Stat(v4Path); err == nil { + if err := copyFile(v4Path, sessionsDatabasePath); err != nil { + return fmt.Errorf("failed to copy a file from %s to %s: %v", v4Path, sessionsDatabasePath, err) + } + // TODO: investigate why copying v4Path to transportDatabasePath does not work and WhisperTransportService + // returns an error. + os.Remove(v4Path) + } + + selectedKeyID := s.w.SelectedKeyPairID() + identity, err := s.w.GetPrivateKey(selectedKeyID) if err != nil { return err } - multideviceConfig := &multidevice.Config{ - InstallationID: s.config.InstallationID, - ProtocolVersion: chat.ProtocolVersion, - MaxInstallations: maxInstallations, + // Create a custom zap.Logger which will forward logs from status-protocol-go to status-go logger. + zapLogger, err := logutils.NewZapLoggerWithAdapter(logutils.Logger()) + if err != nil { + return err } - - addedBundlesHandler := func(addedBundles []*multidevice.Installation) { - handler := PublisherSignalHandler{} - for _, bundle := range addedBundles { - handler.BundleAdded(bundle.Identity, bundle.ID) - } + messenger, err := protocol.NewMessenger( + identity, + &server{server: s.server}, + s.w, + dataDir, + encKey, + s.config.InstallationID, + protocol.WithDatabaseFilePaths( + sessionsDatabasePath, + transportDatabasePath, + ), + protocol.WithGenericDiscoveryTopicSupport(), + protocol.WithCustomLogger(zapLogger), + ) + if err != nil { + return err } + s.messenger = messenger + // Start a loop that retrieves all messages and propagates them to status-react. + s.cancelMessenger = make(chan struct{}) + go s.retrieveMessagesLoop(time.Second, s.cancelMessenger) - protocolService := chat.NewProtocolService( - chat.NewEncryptionService( - persistence, - chat.DefaultEncryptionServiceConfig(s.config.InstallationID)), - sharedsecret.NewService(persistence.GetSharedSecretStorage()), - multidevice.New(multideviceConfig, persistence.GetMultideviceStorage()), - addedBundlesHandler, - s.ProcessNegotiatedSecret) + return nil +} - onNewMessagesHandler := func(messages []*filter.Messages) { - var signalMessages []*signal.Messages - for _, chatMessages := range messages { - signalMessage := &signal.Messages{ - Error: chatMessages.Error, - Chat: chatMessages.Chat, - } - signalMessages = append(signalMessages, signalMessage) - dedupMessages, err := s.processReceivedMessages(chatMessages.Messages) +func (s *Service) retrieveMessagesLoop(tick time.Duration, cancel <-chan struct{}) { + ticker := time.NewTicker(tick) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + chatWithMessages, err := s.messenger.RetrieveRawAll() if err != nil { - log.Error("could not process messages", "err", err) + log.Error("failed to retrieve raw messages", "err", err) continue } - signalMessage.Messages = dedupMessages + var signalMessages []*signal.Messages + for chat, messages := range chatWithMessages { + signalMessage := &signal.Messages{ + Chat: chat, + Error: nil, // TODO: what is it needed for? + Messages: s.deduplicator.Deduplicate(messages), + } + signalMessages = append(signalMessages, signalMessage) + } + + log.Debug("retrieve messages loop", "messages", len(signalMessages)) + + if len(signalMessages) == 0 { + continue + } + + PublisherSignalHandler{}.NewMessages(signalMessages) + case <-cancel: + return } - PublisherSignalHandler{}.NewMessages(signalMessages) } - s.Publisher.Init(persistence.DB, protocolService, onNewMessagesHandler) - return s.Publisher.Start(s.online, true) } -func (s *Service) processReceivedMessages(messages []*whisper.Message) ([]dedup.DeduplicateMessage, error) { - dedupMessages := s.deduplicator.Deduplicate(messages) +func (s *Service) ConfirmMessagesProcessed(messageIDs [][]byte) error { + return s.messenger.ConfirmMessagesProcessed(messageIDs) +} - // Attempt to decrypt message, otherwise leave unchanged - for _, dedupMessage := range dedupMessages { - err := s.ProcessMessage(dedupMessage.Message, dedupMessage.DedupID) - switch err { - case chat.ErrNotPairedDevice: - log.Info("Received a message from non-paired device", "err", err) - case chat.ErrDeviceNotFound: - log.Warn("Received a message not targeted to us", "err", err) - default: - if err != nil { - log.Error("Failed handling message with error", "err", err) - } - } - } +func (s *Service) EnableInstallation(installationID string) error { + return s.messenger.EnableInstallation(installationID) +} - return dedupMessages, nil +// DisableInstallation disables an installation for multi-device sync. +func (s *Service) DisableInstallation(installationID string) error { + return s.messenger.DisableInstallation(installationID) } // UpdateMailservers updates information about selected mail servers. @@ -297,10 +320,6 @@ func (s *Service) Start(server *p2p.Server) error { return nil } -func (s *Service) online() bool { - return s.server.PeerCount() != 0 -} - // Stop is run when a service is stopped. func (s *Service) Stop() error { log.Info("Stopping shhext service") @@ -313,13 +332,24 @@ func (s *Service) Stop() error { s.requestsRegistry.Clear() s.envelopesMonitor.Stop() s.mailMonitor.Stop() - if s.filter != nil { - if err := s.filter.Stop(); err != nil { - log.Error("Failed to stop filter service with error", "err", err) + + if s.cancelMessenger != nil { + select { + case <-s.cancelMessenger: + // channel already closed + default: + close(s.cancelMessenger) + s.cancelMessenger = nil } } - return s.Publisher.Stop() + if s.messenger != nil { + if err := s.messenger.Shutdown(); err != nil { + return err + } + } + + return nil } func (s *Service) syncMessages(ctx context.Context, mailServerID []byte, r whisper.SyncMailRequest) (resp whisper.SyncEventResponse, err error) { @@ -364,3 +394,9 @@ func (s *Service) syncMessages(ctx context.Context, mailServerID []byte, r whisp } } } + +func (s *Service) afterPost(hash []byte, newMessage whisper.NewMessage) hexutil.Bytes { + s.envelopesMonitor.Add(common.BytesToHash(hash), newMessage) + mID := messageID(newMessage) + return mID[:] +} diff --git a/services/shhext/service_test.go b/services/shhext/service_test.go index 84ccb20db..7034c2bca 100644 --- a/services/shhext/service_test.go +++ b/services/shhext/service_test.go @@ -3,8 +3,10 @@ package shhext import ( "context" "encoding/hex" + "encoding/json" "errors" "fmt" + "github.com/status-im/status-go/signal" "io/ioutil" "math" "net" @@ -115,6 +117,12 @@ func (s *ShhExtSuite) SetupTest() { stack, err := node.New(cfg) s.NoError(err) s.whisper[i] = whisper.New(nil) + + privateKey, err := crypto.GenerateKey() + s.NoError(err) + err = s.whisper[i].SelectKeyPair(privateKey) + s.NoError(err) + s.NoError(stack.Register(func(n *node.ServiceContext) (node.Service, error) { return s.whisper[i], nil })) @@ -152,7 +160,14 @@ func (s *ShhExtSuite) TestInitProtocol() { } db, err := leveldb.Open(storage.NewMemStorage(), nil) s.Require().NoError(err) - service := New(whisper.New(nil), nil, db, config) + + shh := whisper.New(nil) + privateKey, err := crypto.GenerateKey() + s.Require().NoError(err) + err = shh.SelectKeyPair(privateKey) + s.Require().NoError(err) + + service := New(shh, nil, db, config) err = service.InitProtocolWithPassword("example-address", "`090///\nhtaa\rhta9x8923)$$'23") s.NoError(err) @@ -344,6 +359,10 @@ func (s *ShhExtSuite) TestRequestMessagesSuccess() { var err error shh := whisper.New(nil) + privateKey, err := crypto.GenerateKey() + s.Require().NoError(err) + err = shh.SelectKeyPair(privateKey) + s.Require().NoError(err) aNode, err := node.New(&node.Config{ P2P: p2p.Config{ MaxPeers: math.MaxInt32, @@ -418,6 +437,94 @@ func (s *ShhExtSuite) TestRequestMessagesSuccess() { s.Require().NoError(waitForHashInMonitor(api.service.mailMonitor, common.BytesToHash(hash), MailServerRequestSent, time.Second)) } +// TestRetrieveMessageLoopNoMessages verifies that there are no signals sent +// if there are no messages. +func (s *ShhExtSuite) TestRetrieveMessageLoopNoMessages() { + shhConfig := whisper.DefaultConfig + shhConfig.MinimumAcceptedPOW = 0 // accept all messages + shh := whisper.New(&shhConfig) + privateKey, err := crypto.GenerateKey() + s.Require().NoError(err) + err = shh.SelectKeyPair(privateKey) + s.Require().NoError(err) + aNode, err := node.New(&node.Config{ + P2P: p2p.Config{ + MaxPeers: math.MaxInt32, + NoDiscovery: true, + }, + NoUSB: true, + }) // in-memory node as no data dir + s.Require().NoError(err) + err = aNode.Register(func(*node.ServiceContext) (node.Service, error) { return shh, nil }) + s.Require().NoError(err) + + err = aNode.Start() + s.Require().NoError(err) + defer func() { err := aNode.Stop(); s.NoError(err) }() + + mock := newHandlerMock(1) + config := params.ShhextConfig{ + InstallationID: "1", + BackupDisabledDataDir: os.TempDir(), + PFSEnabled: true, + } + db, err := leveldb.Open(storage.NewMemStorage(), nil) + s.Require().NoError(err) + service := New(shh, mock, db, config) + s.Require().NoError(service.InitProtocolWithPassword("abc", "password")) + + testCases := []struct { + name string + signalName string + action func() + expectedValue int + }{ + { + name: "send one public message", + signalName: signal.EventNewMessages, + action: func() { + api := NewPublicAPI(service) + _, err = api.SendPublicMessage(context.Background(), SendPublicMessageRPC{ + Chat: "test", + Payload: []byte("abc"), + }) + s.Require().NoError(err) + }, + expectedValue: 1, + }, + { + name: "no messages", + action: func() {}, + expectedValue: 0, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + // Verify a proper signal is sent when a message is received. + var counter int64 + signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) { + var envelope signal.Envelope + err := json.Unmarshal([]byte(jsonEvent), &envelope) + s.NoError(err) + + switch envelope.Type { + case signal.EventNewMessages: + atomic.AddInt64(&counter, 1) + } + }) + + tc.action() + + cancel := make(chan struct{}) + go service.retrieveMessagesLoop(time.Millisecond*10, cancel) + time.Sleep(time.Millisecond * 100) + close(cancel) + s.Require().EqualValues(tc.expectedValue, counter) + }) + } +} + func (s *ShhExtSuite) TearDown() { for _, n := range s.nodes { s.NoError(n.Stop()) diff --git a/signal/events_shhext.go b/signal/events_shhext.go index 2c0fe4133..c8301fcca 100644 --- a/signal/events_shhext.go +++ b/signal/events_shhext.go @@ -4,9 +4,10 @@ import ( "encoding/hex" "github.com/ethereum/go-ethereum/common" - "github.com/status-im/status-go/messaging/filter" "github.com/status-im/status-go/services/shhext/dedup" whisper "github.com/status-im/whisper/whisperv6" + + "github.com/status-im/status-protocol-go/transport/whisper/filter" ) const ( @@ -134,7 +135,7 @@ type EnodeDiscoveredSignal struct { type Messages struct { Error error `json:"error"` Messages []dedup.DeduplicateMessage `json:"messages"` - Chat *filter.Chat `json:"chat"` + Chat filter.Chat `json:"chat"` } // SendEnodeDiscovered tiggered when an enode is discovered. diff --git a/static/chat_db_migrations/1536754952_initial_schema.down.sql b/static/chat_db_migrations/1536754952_initial_schema.down.sql deleted file mode 100644 index e458de882..000000000 --- a/static/chat_db_migrations/1536754952_initial_schema.down.sql +++ /dev/null @@ -1,4 +0,0 @@ -DROP TABLE sessions; -DROP TABLE bundles; -DROP TABLE keys; -DROP TABLE ratchet_info; diff --git a/static/chat_db_migrations/1536754952_initial_schema.up.sql b/static/chat_db_migrations/1536754952_initial_schema.up.sql deleted file mode 100644 index 1c6b31830..000000000 --- a/static/chat_db_migrations/1536754952_initial_schema.up.sql +++ /dev/null @@ -1,40 +0,0 @@ -CREATE TABLE sessions ( - dhr BLOB, - dhs_public BLOB, - dhs_private BLOB, - root_chain_key BLOB, - send_chain_key BLOB, - send_chain_n INTEGER, - recv_chain_key BLOB, - recv_chain_n INTEGER, - step INTEGER, - pn INTEGER, - id BLOB NOT NULL PRIMARY KEY, - UNIQUE(id) ON CONFLICT REPLACE -); - -CREATE TABLE keys ( - public_key BLOB NOT NULL, - msg_num INTEGER, - message_key BLOB NOT NULL, - UNIQUE (msg_num, message_key) ON CONFLICT REPLACE -); - -CREATE TABLE bundles ( - identity BLOB NOT NULL, - installation_id TEXT NOT NULL, - private_key BLOB, - signed_pre_key BLOB NOT NULL PRIMARY KEY ON CONFLICT IGNORE, - timestamp UNSIGNED BIG INT NOT NULL, - expired BOOLEAN DEFAULT 0 -); - -CREATE TABLE ratchet_info ( - bundle_id BLOB NOT NULL, - ephemeral_key BLOB, - identity BLOB NOT NULL, - symmetric_key BLOB NOT NULL, - installation_id TEXT NOT NULL, - UNIQUE(bundle_id, identity) ON CONFLICT REPLACE, - FOREIGN KEY (bundle_id) REFERENCES bundles(signed_pre_key) -); diff --git a/static/chat_db_migrations/1539249977_update_ratchet_info.down.sql b/static/chat_db_migrations/1539249977_update_ratchet_info.down.sql deleted file mode 100644 index 22f67da7e..000000000 --- a/static/chat_db_migrations/1539249977_update_ratchet_info.down.sql +++ /dev/null @@ -1,11 +0,0 @@ -DROP TABLE ratchet_info_v2; - -CREATE TABLE ratchet_info ( - bundle_id BLOB NOT NULL, - ephemeral_key BLOB, - identity BLOB NOT NULL, - symmetric_key BLOB NOT NULL, - installation_id TEXT NOT NULL, - UNIQUE(bundle_id, identity) ON CONFLICT REPLACE, - FOREIGN KEY (bundle_id) REFERENCES bundles(signed_pre_key) -); diff --git a/static/chat_db_migrations/1539249977_update_ratchet_info.up.sql b/static/chat_db_migrations/1539249977_update_ratchet_info.up.sql deleted file mode 100644 index ca2a112f5..000000000 --- a/static/chat_db_migrations/1539249977_update_ratchet_info.up.sql +++ /dev/null @@ -1,13 +0,0 @@ -DELETE FROM sessions; -DELETE FROM keys; -DROP TABLE ratchet_info; - -CREATE TABLE ratchet_info_v2 ( - bundle_id BLOB NOT NULL, - ephemeral_key BLOB, - identity BLOB NOT NULL, - symmetric_key BLOB NOT NULL, - installation_id TEXT NOT NULL, - UNIQUE(bundle_id, identity, installation_id) ON CONFLICT REPLACE, - FOREIGN KEY (bundle_id) REFERENCES bundles(signed_pre_key) -); diff --git a/static/chat_db_migrations/1540715431_add_version.down.sql b/static/chat_db_migrations/1540715431_add_version.down.sql deleted file mode 100644 index a6bc43c51..000000000 --- a/static/chat_db_migrations/1540715431_add_version.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE keys DROP COLUMN session_id; -ALTER TABLE sessions DROP COLUMN keys_count; -ALTER TABLE bundles DROP COLUMN version; diff --git a/static/chat_db_migrations/1540715431_add_version.up.sql b/static/chat_db_migrations/1540715431_add_version.up.sql deleted file mode 100644 index 574eb99e5..000000000 --- a/static/chat_db_migrations/1540715431_add_version.up.sql +++ /dev/null @@ -1,5 +0,0 @@ -DELETE FROM keys; -ALTER TABLE keys ADD COLUMN seq_num INTEGER NOT NULL DEFAULT 0; -ALTER TABLE keys ADD COLUMN session_id BLOB; -ALTER TABLE sessions ADD COLUMN keys_count INTEGER NOT NULL DEFAULT 0; -ALTER TABLE bundles ADD COLUMN version INTEGER NOT NULL DEFAULT 0; diff --git a/static/chat_db_migrations/1541164797_add_installations.down.sql b/static/chat_db_migrations/1541164797_add_installations.down.sql deleted file mode 100644 index adc8e2e57..000000000 --- a/static/chat_db_migrations/1541164797_add_installations.down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE installations; diff --git a/static/chat_db_migrations/1541164797_add_installations.up.sql b/static/chat_db_migrations/1541164797_add_installations.up.sql deleted file mode 100644 index b35f97659..000000000 --- a/static/chat_db_migrations/1541164797_add_installations.up.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE installations ( - identity BLOB NOT NULL, - installation_id TEXT NOT NULL, - timestamp UNSIGNED BIG INT NOT NULL, - enabled BOOLEAN DEFAULT 1, - UNIQUE(identity, installation_id) ON CONFLICT REPLACE -); diff --git a/static/chat_db_migrations/1558084410_add_secret.down.sql b/static/chat_db_migrations/1558084410_add_secret.down.sql deleted file mode 100644 index 1d1e25a51..000000000 --- a/static/chat_db_migrations/1558084410_add_secret.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -DROP TABLE secret_installation_ids; -DROP TABLE secrets; diff --git a/static/chat_db_migrations/1558084410_add_secret.up.sql b/static/chat_db_migrations/1558084410_add_secret.up.sql deleted file mode 100644 index baf69f08b..000000000 --- a/static/chat_db_migrations/1558084410_add_secret.up.sql +++ /dev/null @@ -1,11 +0,0 @@ -CREATE TABLE secrets ( - identity BLOB NOT NULL PRIMARY KEY ON CONFLICT IGNORE, - secret BLOB NOT NULL -); - -CREATE TABLE secret_installation_ids ( - id TEXT NOT NULL, - identity_id BLOB NOT NULL, - UNIQUE(id, identity_id) ON CONFLICT IGNORE, - FOREIGN KEY (identity_id) REFERENCES secrets(identity) -); diff --git a/static/chat_db_migrations/1558588866_add_version.up.sql b/static/chat_db_migrations/1558588866_add_version.up.sql deleted file mode 100644 index 10c117208..000000000 --- a/static/chat_db_migrations/1558588866_add_version.up.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE installations ADD version INTEGER DEFAULT 0; diff --git a/static/chat_db_migrations/1559627659_add_contact_code.down.sql b/static/chat_db_migrations/1559627659_add_contact_code.down.sql deleted file mode 100644 index 5c891a376..000000000 --- a/static/chat_db_migrations/1559627659_add_contact_code.down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE contact_code_config; diff --git a/static/chat_db_migrations/1559627659_add_contact_code.up.sql b/static/chat_db_migrations/1559627659_add_contact_code.up.sql deleted file mode 100644 index 51ea8a048..000000000 --- a/static/chat_db_migrations/1559627659_add_contact_code.up.sql +++ /dev/null @@ -1,6 +0,0 @@ -CREATE TABLE contact_code_config ( - unique_constraint varchar(1) NOT NULL PRIMARY KEY DEFAULT 'X', - last_published INTEGER NOT NULL DEFAULT 0 -); - -INSERT INTO contact_code_config VALUES ('X', 0); diff --git a/static/chat_db_migrations/1561059285_add_whisper_keys.down.sql b/static/chat_db_migrations/1561059285_add_whisper_keys.down.sql deleted file mode 100644 index f0761da00..000000000 --- a/static/chat_db_migrations/1561059285_add_whisper_keys.down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE whisper_keys; diff --git a/static/chat_db_migrations/1561059285_add_whisper_keys.up.sql b/static/chat_db_migrations/1561059285_add_whisper_keys.up.sql deleted file mode 100644 index 844e77282..000000000 --- a/static/chat_db_migrations/1561059285_add_whisper_keys.up.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE TABLE whisper_keys ( - chat_id TEXT PRIMARY KEY ON CONFLICT IGNORE, - key BLOB NOT NULL -) WITHOUT ROWID; diff --git a/static/chat_db_migrations/1561368210_add_installation_metadata.down.sql b/static/chat_db_migrations/1561368210_add_installation_metadata.down.sql deleted file mode 100644 index ba2c563de..000000000 --- a/static/chat_db_migrations/1561368210_add_installation_metadata.down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE installations_metadata; diff --git a/static/chat_db_migrations/1561368210_add_installation_metadata.up.sql b/static/chat_db_migrations/1561368210_add_installation_metadata.up.sql deleted file mode 100644 index 84f84f7f4..000000000 --- a/static/chat_db_migrations/1561368210_add_installation_metadata.up.sql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE TABLE installation_metadata ( - identity BLOB NOT NULL, - installation_id TEXT NOT NULL, - name TEXT NOT NULL DEFAULT '', - device_type TEXT NOT NULL DEFAULT '', - fcm_token TEXT NOT NULL DEFAULT '', - UNIQUE(identity, installation_id) ON CONFLICT REPLACE -); diff --git a/static/chat_db_migrations/static.go b/static/chat_db_migrations/static.go deleted file mode 100644 index 00f164b9a..000000000 --- a/static/chat_db_migrations/static.go +++ /dev/null @@ -1,4 +0,0 @@ -// Package static embeds static (JS, HTML) resources right into the binaries -package static - -//go:generate go-bindata -pkg migrations -o ../../messaging/db/migrations/bindata.go . diff --git a/vendor/github.com/deckarep/golang-set/threadsafe.go b/vendor/github.com/deckarep/golang-set/threadsafe.go index 002e06af1..269b4ab0c 100644 --- a/vendor/github.com/deckarep/golang-set/threadsafe.go +++ b/vendor/github.com/deckarep/golang-set/threadsafe.go @@ -226,8 +226,14 @@ func (set *threadSafeSet) String() string { func (set *threadSafeSet) PowerSet() Set { set.RLock() - ret := set.s.PowerSet() + unsafePowerSet := set.s.PowerSet().(*threadUnsafeSet) set.RUnlock() + + ret := &threadSafeSet{s: newThreadUnsafeSet()} + for subset := range unsafePowerSet.Iter() { + unsafeSubset := subset.(*threadUnsafeSet) + ret.Add(&threadSafeSet{s: *unsafeSubset}) + } return ret } diff --git a/vendor/github.com/golang-migrate/migrate/v4/Dockerfile b/vendor/github.com/golang-migrate/migrate/v4/Dockerfile index f97ccc06d..d43c463d7 100644 --- a/vendor/github.com/golang-migrate/migrate/v4/Dockerfile +++ b/vendor/github.com/golang-migrate/migrate/v4/Dockerfile @@ -9,7 +9,7 @@ COPY . ./ ENV GO111MODULE=on ENV DATABASES="postgres mysql redshift cassandra spanner cockroachdb clickhouse mongodb sqlserver" -ENV SOURCES="file go_bindata github aws_s3 google_cloud_storage godoc_vfs gitlab" +ENV SOURCES="file go_bindata github github_ee aws_s3 google_cloud_storage godoc_vfs gitlab" RUN go build -a -o build/migrate.linux-386 -ldflags="-X main.Version=${VERSION}" -tags "$DATABASES $SOURCES" ./cmd/migrate diff --git a/vendor/github.com/golang-migrate/migrate/v4/MIGRATIONS.md b/vendor/github.com/golang-migrate/migrate/v4/MIGRATIONS.md index 836388702..3475d8e4e 100644 --- a/vendor/github.com/golang-migrate/migrate/v4/MIGRATIONS.md +++ b/vendor/github.com/golang-migrate/migrate/v4/MIGRATIONS.md @@ -44,9 +44,14 @@ It is suggested that the version number of corresponding `up` and `down` migrati files be equivalent for clarity, but they are allowed to differ so long as the relative ordering of the migrations is preserved. -The migration files are permitted to be empty, so in the event that a migration -is a no-op or is irreversible, it is recommended to still include both migration -files, and either leaving them empty or adding a comment as appropriate. +The migration files are permitted to be "empty", in the event that a migration +is a no-op or is irreversible. It is recommended to still include both migration +files by making the whole migration file consist of a comment. +If your database does not support comments, then deleting the migration file will also work. +Note, an actual empty file (e.g. a 0 byte file) may cause issues with your database since migrate +will attempt to run an empty query. In this case, deleting the migration file will also work. +For the rational of this behavior see: +[#244 (comment)](https://github.com/golang-migrate/migrate/issues/244#issuecomment-510758270) ## Migration Content Format diff --git a/vendor/github.com/golang-migrate/migrate/v4/Makefile b/vendor/github.com/golang-migrate/migrate/v4/Makefile index 3bc0de95a..a0cd24afe 100644 --- a/vendor/github.com/golang-migrate/migrate/v4/Makefile +++ b/vendor/github.com/golang-migrate/migrate/v4/Makefile @@ -1,4 +1,4 @@ -SOURCE ?= file go_bindata github aws_s3 google_cloud_storage godoc_vfs gitlab +SOURCE ?= file go_bindata github github_ee aws_s3 google_cloud_storage godoc_vfs gitlab DATABASE ?= postgres mysql redshift cassandra spanner cockroachdb clickhouse mongodb sqlserver VERSION ?= $(shell git describe --tags 2>/dev/null | cut -c 2-) TEST_FLAGS ?= @@ -27,27 +27,14 @@ test-short: test: @-rm -r $(COVERAGE_DIR) @mkdir $(COVERAGE_DIR) - make test-with-flags TEST_FLAGS='-v -race -covermode atomic -coverprofile $$(COVERAGE_DIR)/_$$(RAND).txt -bench=. -benchmem -timeout 20m' - @echo 'mode: atomic' > $(COVERAGE_DIR)/combined.txt - @cat $(COVERAGE_DIR)/_*.txt | grep -v 'mode: atomic' >> $(COVERAGE_DIR)/combined.txt + make test-with-flags TEST_FLAGS='-v -race -covermode atomic -coverprofile $$(COVERAGE_DIR)/combined.txt -bench=. -benchmem -timeout 20m' test-with-flags: - @echo SOURCE: $(SOURCE) + @echo SOURCE: $(SOURCE) @echo DATABASE: $(DATABASE) - @go test $(TEST_FLAGS) . - @go test $(TEST_FLAGS) ./cli/... - @go test $(TEST_FLAGS) ./database - @go test $(TEST_FLAGS) ./testing/... - - @echo -n '$(SOURCE)' | tr -s ' ' '\n' | xargs -I{} go test $(TEST_FLAGS) ./source/{} - @go test $(TEST_FLAGS) ./source/testing/... - @go test $(TEST_FLAGS) ./source/stub/... - - @echo -n '$(DATABASE)' | tr -s ' ' '\n' | xargs -I{} go test $(TEST_FLAGS) ./database/{} - @go test $(TEST_FLAGS) ./database/testing/... - @go test $(TEST_FLAGS) ./database/stub/... + @go test $(TEST_FLAGS) ./... kill-orphaned-docker-containers: @@ -84,7 +71,7 @@ rewrite-import-paths: docs: -make kill-docs nohup godoc -play -http=127.0.0.1:6064 /dev/null 2>&1 & echo $$! > .godoc.pid - cat .godoc.pid + cat .godoc.pid kill-docs: diff --git a/vendor/github.com/golang-migrate/migrate/v4/README.md b/vendor/github.com/golang-migrate/migrate/v4/README.md index 0b38a5e38..d251db0ab 100644 --- a/vendor/github.com/golang-migrate/migrate/v4/README.md +++ b/vendor/github.com/golang-migrate/migrate/v4/README.md @@ -6,16 +6,15 @@ ![Supported Go Versions](https://img.shields.io/badge/Go-1.11%2C%201.12-lightgrey.svg) [![GitHub Release](https://img.shields.io/github/release/golang-migrate/migrate.svg)](https://github.com/golang-migrate/migrate/releases) - # migrate __Database migrations written in Go. Use as [CLI](#cli-usage) or import as [library](#use-in-your-go-project).__ - * Migrate reads migrations from [sources](#migration-sources) +* Migrate reads migrations from [sources](#migration-sources) and applies them in correct order to a [database](#databases). - * Drivers are "dumb", migrate glues everything together and makes sure the logic is bulletproof. +* Drivers are "dumb", migrate glues everything together and makes sure the logic is bulletproof. (Keeps the drivers lightweight, too.) - * Database drivers don't assume things or try to correct user input. When in doubt, fail. +* Database drivers don't assume things or try to correct user input. When in doubt, fail. Forked from [mattes/migrate](https://github.com/mattes/migrate) @@ -23,21 +22,21 @@ Forked from [mattes/migrate](https://github.com/mattes/migrate) Database drivers run migrations. [Add a new database?](database/driver.go) - * [PostgreSQL](database/postgres) - * [Redshift](database/redshift) - * [Ql](database/ql) - * [Cassandra](database/cassandra) - * [SQLite](database/sqlite3) ([todo #165](https://github.com/mattes/migrate/issues/165)) - * [MySQL/ MariaDB](database/mysql) - * [Neo4j](database/neo4j) ([todo #167](https://github.com/mattes/migrate/issues/167)) - * [MongoDB](database/mongodb) - * [CrateDB](database/crate) ([todo #170](https://github.com/mattes/migrate/issues/170)) - * [Shell](database/shell) ([todo #171](https://github.com/mattes/migrate/issues/171)) - * [Google Cloud Spanner](database/spanner) - * [CockroachDB](database/cockroachdb) - * [ClickHouse](database/clickhouse) - * [Firebird](database/firebird) ([todo #49](https://github.com/golang-migrate/migrate/issues/49)) - * [MS SQL Server](database/sqlserver) +* [PostgreSQL](database/postgres) +* [Redshift](database/redshift) +* [Ql](database/ql) +* [Cassandra](database/cassandra) +* [SQLite](database/sqlite3) ([todo #165](https://github.com/mattes/migrate/issues/165)) +* [MySQL/ MariaDB](database/mysql) +* [Neo4j](database/neo4j) ([todo #167](https://github.com/mattes/migrate/issues/167)) +* [MongoDB](database/mongodb) +* [CrateDB](database/crate) ([todo #170](https://github.com/mattes/migrate/issues/170)) +* [Shell](database/shell) ([todo #171](https://github.com/mattes/migrate/issues/171)) +* [Google Cloud Spanner](database/spanner) +* [CockroachDB](database/cockroachdb) +* [ClickHouse](database/clickhouse) +* [Firebird](database/firebird) ([todo #49](https://github.com/golang-migrate/migrate/issues/49)) +* [MS SQL Server](database/sqlserver) ### Database URLs @@ -49,6 +48,7 @@ Explicitly, the following characters need to be escaped: `!`, `#`, `$`, `%`, `&`, `'`, `(`, `)`, `*`, `+`, `,`, `/`, `:`, `;`, `=`, `?`, `@`, `[`, `]` It's easiest to always run the URL parts of your DB connection URL (e.g. username, password, etc) through an URL encoder. See the example Python snippets below: + ```bash $ python3 -c 'import urllib.parse; print(urllib.parse.quote(input("String to encode: "), ""))' String to encode: FAKEpassword!#$%&'()*+,/:;=?@[] @@ -63,44 +63,43 @@ $ Source drivers read migrations from local or remote sources. [Add a new source?](source/driver.go) - * [Filesystem](source/file) - read from fileystem - * [Go-Bindata](source/go_bindata) - read from embedded binary data ([jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata)) - * [Github](source/github) - read from remote Github repositories - * [Gitlab](source/gitlab) - read from remote Gitlab repositories - * [AWS S3](source/aws_s3) - read from Amazon Web Services S3 - * [Google Cloud Storage](source/google_cloud_storage) - read from Google Cloud Platform Storage - - +* [Filesystem](source/file) - read from filesystem +* [Go-Bindata](source/go_bindata) - read from embedded binary data ([jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata)) +* [Github](source/github) - read from remote Github repositories +* [Github Enterprise](source/github_ee) - read from remote Github Enterprise repositories +* [Gitlab](source/gitlab) - read from remote Gitlab repositories +* [AWS S3](source/aws_s3) - read from Amazon Web Services S3 +* [Google Cloud Storage](source/google_cloud_storage) - read from Google Cloud Platform Storage ## CLI usage - * Simple wrapper around this library. - * Handles ctrl+c (SIGINT) gracefully. - * No config search paths, no config files, no magic ENV var injections. +* Simple wrapper around this library. +* Handles ctrl+c (SIGINT) gracefully. +* No config search paths, no config files, no magic ENV var injections. -__[CLI Documentation](cli)__ +__[CLI Documentation](cmd/migrate)__ -### Basic usage: +### Basic usage -``` +```bash $ migrate -source file://path/to/migrations -database postgres://localhost:5432/database up 2 ``` ### Docker usage -``` +```bash $ docker run -v {{ migration dir }}:/migrations --network host migrate/migrate -path=/migrations/ -database postgres://localhost:5432/database up 2 ``` ## Use in your Go project - * API is stable and frozen for this release (v3 & v4). - * Uses [Go modules](https://golang.org/cmd/go/#hdr-Modules__module_versions__and_more) to manage dependencies. - * To help prevent database corruptions, it supports graceful stops via `GracefulStop chan bool`. - * Bring your own logger. - * Uses `io.Reader` streams internally for low memory overhead. - * Thread-safe and no goroutine leaks. +* API is stable and frozen for this release (v3 & v4). +* Uses [Go modules](https://golang.org/cmd/go/#hdr-Modules__module_versions__and_more) to manage dependencies. +* To help prevent database corruptions, it supports graceful stops via `GracefulStop chan bool`. +* Bring your own logger. +* Uses `io.Reader` streams internally for low memory overhead. +* Thread-safe and no goroutine leaks. __[Go Documentation](https://godoc.org/github.com/golang-migrate/migrate)__ @@ -144,7 +143,7 @@ func main() { Each migration has an up and down migration. [Why?](FAQ.md#why-two-separate-files-up-and-down-for-a-migration) -``` +```bash 1481574547_create_users_table.up.sql 1481574547_create_users_table.down.sql ``` @@ -166,8 +165,6 @@ read the [development guide](CONTRIBUTING.md). Also have a look at the [FAQ](FAQ.md). - - --- Looking for alternatives? [https://awesome-go.com/#database](https://awesome-go.com/#database). diff --git a/vendor/github.com/golang-migrate/migrate/v4/migrate.go b/vendor/github.com/golang-migrate/migrate/v4/migrate.go index a4a694b40..3ede504e5 100644 --- a/vendor/github.com/golang-migrate/migrate/v4/migrate.go +++ b/vendor/github.com/golang-migrate/migrate/v4/migrate.go @@ -950,7 +950,7 @@ func (m *Migrate) unlock() error { // if a prevErr is not nil. func (m *Migrate) unlockErr(prevErr error) error { if err := m.unlock(); err != nil { - return NewMultiError(prevErr, err) + return multierror.Append(prevErr, err) } return prevErr } diff --git a/vendor/github.com/golang-migrate/migrate/v4/util.go b/vendor/github.com/golang-migrate/migrate/v4/util.go index 4aeb0f221..1cef03cd8 100644 --- a/vendor/github.com/golang-migrate/migrate/v4/util.go +++ b/vendor/github.com/golang-migrate/migrate/v4/util.go @@ -8,11 +8,16 @@ import ( ) // MultiError holds multiple errors. +// +// Deprecated: Use github.com/hashicorp/go-multierror instead type MultiError struct { Errs []error } // NewMultiError returns an error type holding multiple errors. +// +// Deprecated: Use github.com/hashicorp/go-multierror instead +// func NewMultiError(errs ...error) MultiError { compactErrs := make([]error, 0) for _, e := range errs { diff --git a/vendor/github.com/leodido/go-urn/.gitignore b/vendor/github.com/leodido/go-urn/.gitignore new file mode 100644 index 000000000..a30b5ab04 --- /dev/null +++ b/vendor/github.com/leodido/go-urn/.gitignore @@ -0,0 +1,9 @@ +*.exe +*.dll +*.so +*.dylib + +*.test + +*.out +*.txt \ No newline at end of file diff --git a/vendor/github.com/leodido/go-urn/.travis.yml b/vendor/github.com/leodido/go-urn/.travis.yml new file mode 100644 index 000000000..913b6418b --- /dev/null +++ b/vendor/github.com/leodido/go-urn/.travis.yml @@ -0,0 +1,15 @@ +language: go + +go: + - 1.9.x + - 1.10.x + - tip + +before_install: + - go get -t -v ./... + +script: + - go test -race -coverprofile=coverage.txt -covermode=atomic + +after_success: + - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/vendor/github.com/leodido/go-urn/README.md b/vendor/github.com/leodido/go-urn/README.md new file mode 100644 index 000000000..cc902ec0e --- /dev/null +++ b/vendor/github.com/leodido/go-urn/README.md @@ -0,0 +1,55 @@ +[![Build](https://img.shields.io/travis/leodido/go-urn/master.svg?style=for-the-badge)](https://travis-ci.org/leodido/go-urn) [![Coverage](https://img.shields.io/codecov/c/github/leodido/go-urn.svg?style=for-the-badge)](https://codecov.io/gh/leodido/go-urn) [![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg?style=for-the-badge)](https://godoc.org/github.com/leodido/go-urn) + +**A parser for URNs**. + +> As seen on [RFC 2141](https://tools.ietf.org/html/rfc2141#ref-1). + +[API documentation](https://godoc.org/github.com/leodido/go-urn). + +## Installation + +``` +go get github.com/leodido/go-urn +``` + +## Performances + +This implementation results to be really fast. + +Usually below ½ microsecond on my machine[1](#mymachine). + +Notice it also performs, while parsing: + +1. fine-grained and informative erroring +2. specific-string normalization + +``` +ok/00/urn:a:b______________________________________/-4 20000000 265 ns/op 182 B/op 6 allocs/op +ok/01/URN:foo:a123,456_____________________________/-4 30000000 296 ns/op 200 B/op 6 allocs/op +ok/02/urn:foo:a123%2c456___________________________/-4 20000000 331 ns/op 208 B/op 6 allocs/op +ok/03/urn:ietf:params:scim:schemas:core:2.0:User___/-4 20000000 430 ns/op 280 B/op 6 allocs/op +ok/04/urn:ietf:params:scim:schemas:extension:enterp/-4 20000000 411 ns/op 312 B/op 6 allocs/op +ok/05/urn:ietf:params:scim:schemas:extension:enterp/-4 20000000 472 ns/op 344 B/op 6 allocs/op +ok/06/urn:burnout:nss______________________________/-4 30000000 257 ns/op 192 B/op 6 allocs/op +ok/07/urn:abcdefghilmnopqrstuvzabcdefghilm:x_______/-4 20000000 375 ns/op 213 B/op 6 allocs/op +ok/08/urn:urnurnurn:urn____________________________/-4 30000000 265 ns/op 197 B/op 6 allocs/op +ok/09/urn:ciao:@!=%2c(xyz)+a,b.*@g=$_'_____________/-4 20000000 307 ns/op 248 B/op 6 allocs/op +ok/10/URN:x:abc%1dz%2f%3az_________________________/-4 30000000 259 ns/op 212 B/op 6 allocs/op +no/11/URN:-xxx:x___________________________________/-4 20000000 445 ns/op 320 B/op 6 allocs/op +no/12/urn::colon:nss_______________________________/-4 20000000 461 ns/op 320 B/op 6 allocs/op +no/13/urn:abcdefghilmnopqrstuvzabcdefghilmn:specifi/-4 10000000 660 ns/op 320 B/op 6 allocs/op +no/14/URN:a!?:x____________________________________/-4 20000000 507 ns/op 320 B/op 6 allocs/op +no/15/urn:urn:NSS__________________________________/-4 20000000 429 ns/op 288 B/op 6 allocs/op +no/16/urn:white_space:NSS__________________________/-4 20000000 482 ns/op 320 B/op 6 allocs/op +no/17/urn:concat:no_spaces_________________________/-4 20000000 539 ns/op 328 B/op 7 allocs/op +no/18/urn:a:/______________________________________/-4 20000000 470 ns/op 320 B/op 7 allocs/op +no/19/urn:UrN:NSS__________________________________/-4 20000000 399 ns/op 288 B/op 6 allocs/op +``` + +--- + +* [1]: Intel Core i7-7600U CPU @ 2.80GHz + +--- + +[![Analytics](https://ga-beacon.appspot.com/UA-49657176-1/go-urn?flat)](https://github.com/igrigorik/ga-beacon) \ No newline at end of file diff --git a/vendor/github.com/leodido/go-urn/machine.go b/vendor/github.com/leodido/go-urn/machine.go new file mode 100644 index 000000000..d621ea6e4 --- /dev/null +++ b/vendor/github.com/leodido/go-urn/machine.go @@ -0,0 +1,1670 @@ +package urn + +import ( + "fmt" +) + +var ( + errPrefix = "expecting the prefix to be the \"urn\" string (whatever case) [col %d]" + errIdentifier = "expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col %d]" + errSpecificString = "expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col %d]" + errNoUrnWithinID = "expecting the identifier to not contain the \"urn\" reserved string [col %d]" + errHex = "expecting the specific string hex chars to be well-formed (%%alnum{2}) [col %d]" + errParse = "parsing error [col %d]" +) + + +const start int = 1 +const first_final int = 44 + +const en_fail int = 46 +const en_main int = 1 + + +// Machine is the interface representing the FSM +type Machine interface { + Error() error + Parse(input []byte) (*URN, error) +} + +type machine struct { + data []byte + cs int + p, pe, eof, pb int + err error + tolower []int +} + +// NewMachine creates a new FSM able to parse RFC 2141 strings. +func NewMachine() Machine { + m := &machine{} + + return m +} + +// Err returns the error that occurred on the last call to Parse. +// +// If the result is nil, then the line was parsed successfully. +func (m *machine) Error() error { + return m.err +} + +func (m *machine) text() []byte { + return m.data[m.pb:m.p] +} + +// Parse parses the input byte array as a RFC 2141 string. +func (m *machine) Parse(input []byte) (*URN, error) { + m.data = input + m.p = 0 + m.pb = 0 + m.pe = len(input) + m.eof = len(input) + m.err = nil + m.tolower = []int{} + output := &URN{} + + { + m.cs = start + } + + + { + if (m.p) == (m.pe) { + goto _test_eof + } + switch m.cs { + case 1: + goto st_case_1 + case 0: + goto st_case_0 + case 2: + goto st_case_2 + case 3: + goto st_case_3 + case 4: + goto st_case_4 + case 5: + goto st_case_5 + case 6: + goto st_case_6 + case 7: + goto st_case_7 + case 8: + goto st_case_8 + case 9: + goto st_case_9 + case 10: + goto st_case_10 + case 11: + goto st_case_11 + case 12: + goto st_case_12 + case 13: + goto st_case_13 + case 14: + goto st_case_14 + case 15: + goto st_case_15 + case 16: + goto st_case_16 + case 17: + goto st_case_17 + case 18: + goto st_case_18 + case 19: + goto st_case_19 + case 20: + goto st_case_20 + case 21: + goto st_case_21 + case 22: + goto st_case_22 + case 23: + goto st_case_23 + case 24: + goto st_case_24 + case 25: + goto st_case_25 + case 26: + goto st_case_26 + case 27: + goto st_case_27 + case 28: + goto st_case_28 + case 29: + goto st_case_29 + case 30: + goto st_case_30 + case 31: + goto st_case_31 + case 32: + goto st_case_32 + case 33: + goto st_case_33 + case 34: + goto st_case_34 + case 35: + goto st_case_35 + case 36: + goto st_case_36 + case 37: + goto st_case_37 + case 38: + goto st_case_38 + case 44: + goto st_case_44 + case 39: + goto st_case_39 + case 40: + goto st_case_40 + case 45: + goto st_case_45 + case 41: + goto st_case_41 + case 42: + goto st_case_42 + case 43: + goto st_case_43 + case 46: + goto st_case_46 + } + goto st_out + st_case_1: + switch (m.data)[(m.p)] { + case 85: + goto tr1 + case 117: + goto tr1 + } + goto tr0 + tr0: + m.err = fmt.Errorf(errParse, m.p) + (m.p)-- + + { + goto st46 + } + + goto st0 + tr3: + m.err = fmt.Errorf(errPrefix, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errParse, m.p) + (m.p)-- + + { + goto st46 + } + + goto st0 + tr6: + m.err = fmt.Errorf(errIdentifier, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errParse, m.p) + (m.p)-- + + { + goto st46 + } + + goto st0 + tr41: + m.err = fmt.Errorf(errSpecificString, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errParse, m.p) + (m.p)-- + + { + goto st46 + } + + goto st0 + tr44: + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errSpecificString, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errParse, m.p) + (m.p)-- + + { + goto st46 + } + + goto st0 + tr50: + m.err = fmt.Errorf(errPrefix, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errIdentifier, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errParse, m.p) + (m.p)-- + + { + goto st46 + } + + goto st0 + tr52: + m.err = fmt.Errorf(errNoUrnWithinID, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errIdentifier, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errParse, m.p) + (m.p)-- + + { + goto st46 + } + + goto st0 + st_case_0: + st0: + m.cs = 0 + goto _out + tr1: + m.pb = m.p + + goto st2 + st2: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof2 + } + st_case_2: + switch (m.data)[(m.p)] { + case 82: + goto st3 + case 114: + goto st3 + } + goto tr0 + st3: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof3 + } + st_case_3: + switch (m.data)[(m.p)] { + case 78: + goto st4 + case 110: + goto st4 + } + goto tr3 + st4: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof4 + } + st_case_4: + if (m.data)[(m.p)] == 58 { + goto tr5 + } + goto tr0 + tr5: + output.prefix = string(m.text()) + + goto st5 + st5: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof5 + } + st_case_5: + switch (m.data)[(m.p)] { + case 85: + goto tr8 + case 117: + goto tr8 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto tr7 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr7 + } + default: + goto tr7 + } + goto tr6 + tr7: + m.pb = m.p + + goto st6 + st6: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof6 + } + st_case_6: + switch (m.data)[(m.p)] { + case 45: + goto st7 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st7 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st7 + } + default: + goto st7 + } + goto tr6 + st7: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof7 + } + st_case_7: + switch (m.data)[(m.p)] { + case 45: + goto st8 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st8 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st8 + } + default: + goto st8 + } + goto tr6 + st8: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof8 + } + st_case_8: + switch (m.data)[(m.p)] { + case 45: + goto st9 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st9 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st9 + } + default: + goto st9 + } + goto tr6 + st9: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof9 + } + st_case_9: + switch (m.data)[(m.p)] { + case 45: + goto st10 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st10 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st10 + } + default: + goto st10 + } + goto tr6 + st10: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof10 + } + st_case_10: + switch (m.data)[(m.p)] { + case 45: + goto st11 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st11 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st11 + } + default: + goto st11 + } + goto tr6 + st11: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof11 + } + st_case_11: + switch (m.data)[(m.p)] { + case 45: + goto st12 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st12 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st12 + } + default: + goto st12 + } + goto tr6 + st12: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof12 + } + st_case_12: + switch (m.data)[(m.p)] { + case 45: + goto st13 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st13 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st13 + } + default: + goto st13 + } + goto tr6 + st13: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof13 + } + st_case_13: + switch (m.data)[(m.p)] { + case 45: + goto st14 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st14 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st14 + } + default: + goto st14 + } + goto tr6 + st14: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof14 + } + st_case_14: + switch (m.data)[(m.p)] { + case 45: + goto st15 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st15 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st15 + } + default: + goto st15 + } + goto tr6 + st15: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof15 + } + st_case_15: + switch (m.data)[(m.p)] { + case 45: + goto st16 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st16 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st16 + } + default: + goto st16 + } + goto tr6 + st16: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof16 + } + st_case_16: + switch (m.data)[(m.p)] { + case 45: + goto st17 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st17 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st17 + } + default: + goto st17 + } + goto tr6 + st17: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof17 + } + st_case_17: + switch (m.data)[(m.p)] { + case 45: + goto st18 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st18 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st18 + } + default: + goto st18 + } + goto tr6 + st18: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof18 + } + st_case_18: + switch (m.data)[(m.p)] { + case 45: + goto st19 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st19 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st19 + } + default: + goto st19 + } + goto tr6 + st19: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof19 + } + st_case_19: + switch (m.data)[(m.p)] { + case 45: + goto st20 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st20 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st20 + } + default: + goto st20 + } + goto tr6 + st20: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof20 + } + st_case_20: + switch (m.data)[(m.p)] { + case 45: + goto st21 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st21 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st21 + } + default: + goto st21 + } + goto tr6 + st21: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof21 + } + st_case_21: + switch (m.data)[(m.p)] { + case 45: + goto st22 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st22 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st22 + } + default: + goto st22 + } + goto tr6 + st22: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof22 + } + st_case_22: + switch (m.data)[(m.p)] { + case 45: + goto st23 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st23 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st23 + } + default: + goto st23 + } + goto tr6 + st23: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof23 + } + st_case_23: + switch (m.data)[(m.p)] { + case 45: + goto st24 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st24 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st24 + } + default: + goto st24 + } + goto tr6 + st24: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof24 + } + st_case_24: + switch (m.data)[(m.p)] { + case 45: + goto st25 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st25 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st25 + } + default: + goto st25 + } + goto tr6 + st25: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof25 + } + st_case_25: + switch (m.data)[(m.p)] { + case 45: + goto st26 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st26 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st26 + } + default: + goto st26 + } + goto tr6 + st26: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof26 + } + st_case_26: + switch (m.data)[(m.p)] { + case 45: + goto st27 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st27 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st27 + } + default: + goto st27 + } + goto tr6 + st27: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof27 + } + st_case_27: + switch (m.data)[(m.p)] { + case 45: + goto st28 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st28 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st28 + } + default: + goto st28 + } + goto tr6 + st28: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof28 + } + st_case_28: + switch (m.data)[(m.p)] { + case 45: + goto st29 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st29 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st29 + } + default: + goto st29 + } + goto tr6 + st29: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof29 + } + st_case_29: + switch (m.data)[(m.p)] { + case 45: + goto st30 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st30 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st30 + } + default: + goto st30 + } + goto tr6 + st30: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof30 + } + st_case_30: + switch (m.data)[(m.p)] { + case 45: + goto st31 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st31 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st31 + } + default: + goto st31 + } + goto tr6 + st31: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof31 + } + st_case_31: + switch (m.data)[(m.p)] { + case 45: + goto st32 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st32 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st32 + } + default: + goto st32 + } + goto tr6 + st32: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof32 + } + st_case_32: + switch (m.data)[(m.p)] { + case 45: + goto st33 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st33 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st33 + } + default: + goto st33 + } + goto tr6 + st33: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof33 + } + st_case_33: + switch (m.data)[(m.p)] { + case 45: + goto st34 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st34 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st34 + } + default: + goto st34 + } + goto tr6 + st34: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof34 + } + st_case_34: + switch (m.data)[(m.p)] { + case 45: + goto st35 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st35 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st35 + } + default: + goto st35 + } + goto tr6 + st35: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof35 + } + st_case_35: + switch (m.data)[(m.p)] { + case 45: + goto st36 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st36 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st36 + } + default: + goto st36 + } + goto tr6 + st36: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof36 + } + st_case_36: + switch (m.data)[(m.p)] { + case 45: + goto st37 + case 58: + goto tr10 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st37 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st37 + } + default: + goto st37 + } + goto tr6 + st37: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof37 + } + st_case_37: + if (m.data)[(m.p)] == 58 { + goto tr10 + } + goto tr6 + tr10: + output.ID = string(m.text()) + + goto st38 + st38: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof38 + } + st_case_38: + switch (m.data)[(m.p)] { + case 33: + goto tr42 + case 36: + goto tr42 + case 37: + goto tr43 + case 61: + goto tr42 + case 95: + goto tr42 + } + switch { + case (m.data)[(m.p)] < 48: + if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { + goto tr42 + } + case (m.data)[(m.p)] > 59: + switch { + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto tr42 + } + case (m.data)[(m.p)] >= 64: + goto tr42 + } + default: + goto tr42 + } + goto tr41 + tr42: + m.pb = m.p + + goto st44 + st44: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof44 + } + st_case_44: + switch (m.data)[(m.p)] { + case 33: + goto st44 + case 36: + goto st44 + case 37: + goto st39 + case 61: + goto st44 + case 95: + goto st44 + } + switch { + case (m.data)[(m.p)] < 48: + if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { + goto st44 + } + case (m.data)[(m.p)] > 59: + switch { + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st44 + } + case (m.data)[(m.p)] >= 64: + goto st44 + } + default: + goto st44 + } + goto tr41 + tr43: + m.pb = m.p + + goto st39 + st39: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof39 + } + st_case_39: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st40 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st40 + } + default: + goto tr46 + } + goto tr44 + tr46: + m.tolower = append(m.tolower, m.p-m.pb) + + goto st40 + st40: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof40 + } + st_case_40: + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st45 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st45 + } + default: + goto tr48 + } + goto tr44 + tr48: + m.tolower = append(m.tolower, m.p-m.pb) + + goto st45 + st45: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof45 + } + st_case_45: + switch (m.data)[(m.p)] { + case 33: + goto st44 + case 36: + goto st44 + case 37: + goto st39 + case 61: + goto st44 + case 95: + goto st44 + } + switch { + case (m.data)[(m.p)] < 48: + if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { + goto st44 + } + case (m.data)[(m.p)] > 59: + switch { + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st44 + } + case (m.data)[(m.p)] >= 64: + goto st44 + } + default: + goto st44 + } + goto tr44 + tr8: + m.pb = m.p + + goto st41 + st41: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof41 + } + st_case_41: + switch (m.data)[(m.p)] { + case 45: + goto st7 + case 58: + goto tr10 + case 82: + goto st42 + case 114: + goto st42 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st7 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st7 + } + default: + goto st7 + } + goto tr6 + st42: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof42 + } + st_case_42: + switch (m.data)[(m.p)] { + case 45: + goto st8 + case 58: + goto tr10 + case 78: + goto st43 + case 110: + goto st43 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st8 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st8 + } + default: + goto st8 + } + goto tr50 + st43: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof43 + } + st_case_43: + if (m.data)[(m.p)] == 45 { + goto st9 + } + switch { + case (m.data)[(m.p)] < 65: + if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { + goto st9 + } + case (m.data)[(m.p)] > 90: + if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { + goto st9 + } + default: + goto st9 + } + goto tr52 + st46: + if (m.p)++; (m.p) == (m.pe) { + goto _test_eof46 + } + st_case_46: + switch (m.data)[(m.p)] { + case 10: + goto st0 + case 13: + goto st0 + } + goto st46 + st_out: + _test_eof2: + m.cs = 2 + goto _test_eof + _test_eof3: + m.cs = 3 + goto _test_eof + _test_eof4: + m.cs = 4 + goto _test_eof + _test_eof5: + m.cs = 5 + goto _test_eof + _test_eof6: + m.cs = 6 + goto _test_eof + _test_eof7: + m.cs = 7 + goto _test_eof + _test_eof8: + m.cs = 8 + goto _test_eof + _test_eof9: + m.cs = 9 + goto _test_eof + _test_eof10: + m.cs = 10 + goto _test_eof + _test_eof11: + m.cs = 11 + goto _test_eof + _test_eof12: + m.cs = 12 + goto _test_eof + _test_eof13: + m.cs = 13 + goto _test_eof + _test_eof14: + m.cs = 14 + goto _test_eof + _test_eof15: + m.cs = 15 + goto _test_eof + _test_eof16: + m.cs = 16 + goto _test_eof + _test_eof17: + m.cs = 17 + goto _test_eof + _test_eof18: + m.cs = 18 + goto _test_eof + _test_eof19: + m.cs = 19 + goto _test_eof + _test_eof20: + m.cs = 20 + goto _test_eof + _test_eof21: + m.cs = 21 + goto _test_eof + _test_eof22: + m.cs = 22 + goto _test_eof + _test_eof23: + m.cs = 23 + goto _test_eof + _test_eof24: + m.cs = 24 + goto _test_eof + _test_eof25: + m.cs = 25 + goto _test_eof + _test_eof26: + m.cs = 26 + goto _test_eof + _test_eof27: + m.cs = 27 + goto _test_eof + _test_eof28: + m.cs = 28 + goto _test_eof + _test_eof29: + m.cs = 29 + goto _test_eof + _test_eof30: + m.cs = 30 + goto _test_eof + _test_eof31: + m.cs = 31 + goto _test_eof + _test_eof32: + m.cs = 32 + goto _test_eof + _test_eof33: + m.cs = 33 + goto _test_eof + _test_eof34: + m.cs = 34 + goto _test_eof + _test_eof35: + m.cs = 35 + goto _test_eof + _test_eof36: + m.cs = 36 + goto _test_eof + _test_eof37: + m.cs = 37 + goto _test_eof + _test_eof38: + m.cs = 38 + goto _test_eof + _test_eof44: + m.cs = 44 + goto _test_eof + _test_eof39: + m.cs = 39 + goto _test_eof + _test_eof40: + m.cs = 40 + goto _test_eof + _test_eof45: + m.cs = 45 + goto _test_eof + _test_eof41: + m.cs = 41 + goto _test_eof + _test_eof42: + m.cs = 42 + goto _test_eof + _test_eof43: + m.cs = 43 + goto _test_eof + _test_eof46: + m.cs = 46 + goto _test_eof + + _test_eof: + { + } + if (m.p) == (m.eof) { + switch m.cs { + case 44, 45: + raw := m.text() + output.SS = string(raw) + // Iterate upper letters lowering them + for _, i := range m.tolower { + raw[i] = raw[i] + 32 + } + output.norm = string(raw) + + case 1, 2, 4: + m.err = fmt.Errorf(errParse, m.p) + (m.p)-- + + { + goto st46 + } + + case 3: + m.err = fmt.Errorf(errPrefix, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errParse, m.p) + (m.p)-- + + { + goto st46 + } + + case 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 41: + m.err = fmt.Errorf(errIdentifier, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errParse, m.p) + (m.p)-- + + { + goto st46 + } + + case 38: + m.err = fmt.Errorf(errSpecificString, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errParse, m.p) + (m.p)-- + + { + goto st46 + } + + case 42: + m.err = fmt.Errorf(errPrefix, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errIdentifier, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errParse, m.p) + (m.p)-- + + { + goto st46 + } + + case 43: + m.err = fmt.Errorf(errNoUrnWithinID, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errIdentifier, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errParse, m.p) + (m.p)-- + + { + goto st46 + } + + case 39, 40: + m.err = fmt.Errorf(errHex, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errSpecificString, m.p) + (m.p)-- + + { + goto st46 + } + + m.err = fmt.Errorf(errParse, m.p) + (m.p)-- + + { + goto st46 + } + + } + } + + _out: + { + } + } + + if m.cs < first_final || m.cs == en_fail { + return nil, m.err + } + + return output, nil +} diff --git a/vendor/github.com/leodido/go-urn/machine.go.rl b/vendor/github.com/leodido/go-urn/machine.go.rl new file mode 100644 index 000000000..3bc05a651 --- /dev/null +++ b/vendor/github.com/leodido/go-urn/machine.go.rl @@ -0,0 +1,159 @@ +package urn + +import ( + "fmt" +) + +var ( + errPrefix = "expecting the prefix to be the \"urn\" string (whatever case) [col %d]" + errIdentifier = "expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col %d]" + errSpecificString = "expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col %d]" + errNoUrnWithinID = "expecting the identifier to not contain the \"urn\" reserved string [col %d]" + errHex = "expecting the specific string hex chars to be well-formed (%%alnum{2}) [col %d]" + errParse = "parsing error [col %d]" +) + +%%{ +machine urn; + +# unsigned alphabet +alphtype uint8; + +action mark { + m.pb = m.p +} + +action tolower { + m.tolower = append(m.tolower, m.p - m.pb) +} + +action set_pre { + output.prefix = string(m.text()) +} + +action set_nid { + output.ID = string(m.text()) +} + +action set_nss { + raw := m.text() + output.SS = string(raw) + // Iterate upper letters lowering them + for _, i := range m.tolower { + raw[i] = raw[i] + 32 + } + output.norm = string(raw) +} + +action err_pre { + m.err = fmt.Errorf(errPrefix, m.p) + fhold; + fgoto fail; +} + +action err_nid { + m.err = fmt.Errorf(errIdentifier, m.p) + fhold; + fgoto fail; +} + +action err_nss { + m.err = fmt.Errorf(errSpecificString, m.p) + fhold; + fgoto fail; +} + +action err_urn { + m.err = fmt.Errorf(errNoUrnWithinID, m.p) + fhold; + fgoto fail; +} + +action err_hex { + m.err = fmt.Errorf(errHex, m.p) + fhold; + fgoto fail; +} + +action err_parse { + m.err = fmt.Errorf(errParse, m.p) + fhold; + fgoto fail; +} + +pre = ([uU][rR][nN] @err(err_pre)) >mark %set_pre; + +nid = (alnum >mark (alnum | '-'){0,31}) %set_nid; + +hex = '%' (digit | lower | upper >tolower){2} $err(err_hex); + +sss = (alnum | [()+,\-.:=@;$_!*']); + +nss = (sss | hex)+ $err(err_nss); + +fail := (any - [\n\r])* @err{ fgoto main; }; + +main := (pre ':' (nid - pre %err(err_urn)) $err(err_nid) ':' nss >mark %set_nss) $err(err_parse); + +}%% + +%% write data noerror noprefix; + +// Machine is the interface representing the FSM +type Machine interface { + Error() error + Parse(input []byte) (*URN, error) +} + +type machine struct { + data []byte + cs int + p, pe, eof, pb int + err error + tolower []int +} + +// NewMachine creates a new FSM able to parse RFC 2141 strings. +func NewMachine() Machine { + m := &machine{} + + %% access m.; + %% variable p m.p; + %% variable pe m.pe; + %% variable eof m.eof; + %% variable data m.data; + + return m +} + +// Err returns the error that occurred on the last call to Parse. +// +// If the result is nil, then the line was parsed successfully. +func (m *machine) Error() error { + return m.err +} + +func (m *machine) text() []byte { + return m.data[m.pb:m.p] +} + +// Parse parses the input byte array as a RFC 2141 string. +func (m *machine) Parse(input []byte) (*URN, error) { + m.data = input + m.p = 0 + m.pb = 0 + m.pe = len(input) + m.eof = len(input) + m.err = nil + m.tolower = []int{} + output := &URN{} + + %% write init; + %% write exec; + + if m.cs < first_final || m.cs == en_fail { + return nil, m.err + } + + return output, nil +} diff --git a/vendor/github.com/leodido/go-urn/makefile b/vendor/github.com/leodido/go-urn/makefile new file mode 100644 index 000000000..362137ad2 --- /dev/null +++ b/vendor/github.com/leodido/go-urn/makefile @@ -0,0 +1,17 @@ +SHELL := /bin/bash + +machine.go: machine.go.rl + ragel -Z -G2 -e -o $@ $< + @gofmt -w -s $@ + @sed -i '/^\/\/line/d' $@ + +.PHONY: build +build: machine.go + +.PHONY: bench +bench: *_test.go machine.go + go test -bench=. -benchmem -benchtime=5s ./... + +.PHONY: tests +tests: *_test.go machine.go + go test -race -timeout 10s -coverprofile=coverage.out -covermode=atomic -v ./... \ No newline at end of file diff --git a/vendor/github.com/leodido/go-urn/urn.go b/vendor/github.com/leodido/go-urn/urn.go new file mode 100644 index 000000000..b903b7b3c --- /dev/null +++ b/vendor/github.com/leodido/go-urn/urn.go @@ -0,0 +1,63 @@ +package urn + +import ( + "strings" +) + +// URN represents an Uniform Resource Name. +// +// The general form represented is: +// +// urn:: +// +// Details at https://tools.ietf.org/html/rfc2141. +type URN struct { + prefix string // Static prefix. Equal to "urn" when empty. + ID string // Namespace identifier + SS string // Namespace specific string + norm string // Normalized namespace specific string +} + +// Normalize turns the receiving URN into its norm version. +// +// Which means: lowercase prefix, lowercase namespace identifier, and immutate namespace specific string chars (except tokens which are lowercased). +func (u *URN) Normalize() *URN { + return &URN{ + prefix: "urn", + ID: strings.ToLower(u.ID), + SS: u.norm, + } +} + +// Equal checks the lexical equivalence of the current URN with another one. +func (u *URN) Equal(x *URN) bool { + return *u.Normalize() == *x.Normalize() +} + +// String reassembles the URN into a valid URN string. +// +// This requires both ID and SS fields to be non-empty. +// Otherwise it returns an empty string. +// +// Default URN prefix is "urn". +func (u *URN) String() string { + var res string + if u.ID != "" && u.SS != "" { + if u.prefix == "" { + res += "urn" + } + res += u.prefix + ":" + u.ID + ":" + u.SS + } + + return res +} + +// Parse is responsible to create an URN instance from a byte array matching the correct URN syntax. +func Parse(u []byte) (*URN, bool) { + urn, err := NewMachine().Parse(u) + if err != nil { + return nil, false + } + + return urn, true +} diff --git a/vendor/github.com/rs/cors/.travis.yml b/vendor/github.com/rs/cors/.travis.yml index bbb5185a2..17e5e50d5 100644 --- a/vendor/github.com/rs/cors/.travis.yml +++ b/vendor/github.com/rs/cors/.travis.yml @@ -1,4 +1,8 @@ language: go go: -- 1.3 -- 1.4 +- 1.9 +- "1.10" +- tip +matrix: + allow_failures: + - go: tip diff --git a/vendor/github.com/rs/cors/README.md b/vendor/github.com/rs/cors/README.md index 4bf56724e..ecc83b295 100644 --- a/vendor/github.com/rs/cors/README.md +++ b/vendor/github.com/rs/cors/README.md @@ -49,6 +49,14 @@ The server now runs on `localhost:8080`: {"hello": "world"} +### Allow * With Credentials Security Protection + +This library has been modified to avoid a well known security issue when configured with `AllowedOrigins` to `*` and `AllowCredentials` to `true`. Such setup used to make the library reflects the request `Origin` header value, working around a security protection embedded into the standard that makes clients to refuse such configuration. This behavior has been removed with [#55](https://github.com/rs/cors/issues/55) and [#57](https://github.com/rs/cors/issues/57). + +If you depend on this behavior and understand the implications, you can restore it using the `AllowOriginFunc` with `func(origin string) {return true}`. + +Please refer to [#55](https://github.com/rs/cors/issues/55) for more information about the security implications. + ### More Examples * `net/http`: [examples/nethttp/server.go](https://github.com/rs/cors/blob/master/examples/nethttp/server.go) @@ -56,6 +64,11 @@ The server now runs on `localhost:8080`: * [Martini](http://martini.codegangsta.io): [examples/martini/server.go](https://github.com/rs/cors/blob/master/examples/martini/server.go) * [Negroni](https://github.com/codegangsta/negroni): [examples/negroni/server.go](https://github.com/rs/cors/blob/master/examples/negroni/server.go) * [Alice](https://github.com/justinas/alice): [examples/alice/server.go](https://github.com/rs/cors/blob/master/examples/alice/server.go) +* [HttpRouter](https://github.com/julienschmidt/httprouter): [examples/httprouter/server.go](https://github.com/rs/cors/blob/master/examples/httprouter/server.go) +* [Gorilla](http://www.gorillatoolkit.org/pkg/mux): [examples/gorilla/server.go](https://github.com/rs/cors/blob/master/examples/gorilla/server.go) +* [Buffalo](https://gobuffalo.io): [examples/buffalo/server.go](https://github.com/rs/cors/blob/master/examples/buffalo/server.go) +* [Gin](https://gin-gonic.github.io/gin): [examples/gin/server.go](https://github.com/rs/cors/blob/master/examples/gin/server.go) +* [Chi](https://github.com/go-chi/chi): [examples/chi/server.go](https://github.com/rs/cors/blob/master/examples/chi/server.go) ## Parameters @@ -63,8 +76,10 @@ Parameters are passed to the middleware thru the `cors.New` method as follow: ```go c := cors.New(cors.Options{ - AllowedOrigins: []string{"http://foo.com"}, + AllowedOrigins: []string{"http://foo.com", "http://foo.com:8080"}, AllowCredentials: true, + // Enable Debugging for testing, consider disabling in production + Debug: true, }) // Insert the middleware @@ -72,7 +87,8 @@ handler = c.Handler(handler) ``` * **AllowedOrigins** `[]string`: A list of origins a cross-domain request can be executed from. If the special `*` value is present in the list, all origins will be allowed. An origin may contain a wildcard (`*`) to replace 0 or more characters (i.e.: `http://*.domain.com`). Usage of wildcards implies a small performance penality. Only one wildcard can be used per origin. The default value is `*`. -* **AllowOriginFunc** `func (origin string) bool`: A custom function to validate the origin. It take the origin as argument and returns true if allowed or false otherwise. If this option is set, the content of `AllowedOrigins` is ignored +* **AllowOriginFunc** `func (origin string) bool`: A custom function to validate the origin. It takes the origin as an argument and returns true if allowed, or false otherwise. If this option is set, the content of `AllowedOrigins` is ignored. +* **AllowOriginRequestFunc** `func (r *http.Request origin string) bool`: A custom function to validate the origin. It takes the HTTP Request object and the origin as argument and returns true if allowed or false otherwise. If this option is set, the content of `AllowedOrigins` and `AllowOriginFunc` is ignored * **AllowedMethods** `[]string`: A list of methods the client is allowed to use with cross-domain requests. Default value is simple methods (`GET` and `POST`). * **AllowedHeaders** `[]string`: A list of non simple headers the client is allowed to use with cross-domain requests. * **ExposedHeaders** `[]string`: Indicates which headers are safe to expose to the API of a CORS API specification diff --git a/vendor/github.com/rs/cors/cors.go b/vendor/github.com/rs/cors/cors.go index 4bb22d8fc..d301ca724 100644 --- a/vendor/github.com/rs/cors/cors.go +++ b/vendor/github.com/rs/cors/cors.go @@ -26,9 +26,6 @@ import ( "os" "strconv" "strings" - - "github.com/rs/xhandler" - "golang.org/x/net/context" ) // Options is a configuration container to setup the CORS middleware. @@ -36,7 +33,7 @@ type Options struct { // AllowedOrigins is a list of origins a cross-domain request can be executed from. // If the special "*" value is present in the list, all origins will be allowed. // An origin may contain a wildcard (*) to replace 0 or more characters - // (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality. + // (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penalty. // Only one wildcard can be used per origin. // Default value is ["*"] AllowedOrigins []string @@ -44,8 +41,12 @@ type Options struct { // as argument and returns true if allowed or false otherwise. If this option is // set, the content of AllowedOrigins is ignored. AllowOriginFunc func(origin string) bool + // AllowOriginFunc is a custom function to validate the origin. It takes the HTTP Request object and the origin as + // argument and returns true if allowed or false otherwise. If this option is set, the content of `AllowedOrigins` + // and `AllowOriginFunc` is ignored. + AllowOriginRequestFunc func(r *http.Request, origin string) bool // AllowedMethods is a list of methods the client is allowed to use with - // cross-domain requests. Default value is simple methods (GET and POST) + // cross-domain requests. Default value is simple methods (HEAD, GET and POST). AllowedMethods []string // AllowedHeaders is list of non simple headers the client is allowed to use with // cross-domain requests. @@ -55,12 +56,12 @@ type Options struct { // ExposedHeaders indicates which headers are safe to expose to the API of a CORS // API specification ExposedHeaders []string - // AllowCredentials indicates whether the request can include user credentials like - // cookies, HTTP authentication or client side SSL certificates. - AllowCredentials bool // MaxAge indicates how long (in seconds) the results of a preflight request // can be cached MaxAge int + // AllowCredentials indicates whether the request can include user credentials like + // cookies, HTTP authentication or client side SSL certificates. + AllowCredentials bool // OptionsPassthrough instructs preflight to let other potential next handlers to // process the OPTIONS method. Turn this on if your application handles OPTIONS. OptionsPassthrough bool @@ -72,35 +73,38 @@ type Options struct { type Cors struct { // Debug logger Log *log.Logger - // Set to true when allowed origins contains a "*" - allowedOriginsAll bool // Normalized list of plain allowed origins allowedOrigins []string // List of allowed origins containing wildcards allowedWOrigins []wildcard // Optional origin validator function allowOriginFunc func(origin string) bool - // Set to true when allowed headers contains a "*" - allowedHeadersAll bool + // Optional origin validator (with request) function + allowOriginRequestFunc func(r *http.Request, origin string) bool // Normalized list of allowed headers allowedHeaders []string // Normalized list of allowed methods allowedMethods []string // Normalized list of exposed headers - exposedHeaders []string + exposedHeaders []string + maxAge int + // Set to true when allowed origins contains a "*" + allowedOriginsAll bool + // Set to true when allowed headers contains a "*" + allowedHeadersAll bool allowCredentials bool - maxAge int optionPassthrough bool } // New creates a new Cors handler with the provided options. func New(options Options) *Cors { c := &Cors{ - exposedHeaders: convert(options.ExposedHeaders, http.CanonicalHeaderKey), - allowOriginFunc: options.AllowOriginFunc, - allowCredentials: options.AllowCredentials, - maxAge: options.MaxAge, - optionPassthrough: options.OptionsPassthrough, + exposedHeaders: convert(options.ExposedHeaders, http.CanonicalHeaderKey), + allowOriginFunc: options.AllowOriginFunc, + allowOriginRequestFunc: options.AllowOriginRequestFunc, + allowCredentials: options.AllowCredentials, + maxAge: options.MaxAge, + optionPassthrough: options.OptionsPassthrough, } if options.Debug { c.Log = log.New(os.Stdout, "[cors] ", log.LstdFlags) @@ -112,8 +116,10 @@ func New(options Options) *Cors { // Allowed Origins if len(options.AllowedOrigins) == 0 { - // Default is all origins - c.allowedOriginsAll = true + if options.AllowOriginFunc == nil && options.AllowOriginRequestFunc == nil { + // Default is all origins + c.allowedOriginsAll = true + } } else { c.allowedOrigins = []string{} c.allowedWOrigins = []wildcard{} @@ -128,7 +134,7 @@ func New(options Options) *Cors { break } else if i := strings.IndexByte(origin, '*'); i >= 0 { // Split the origin in two: start and end string without the * - w := wildcard{origin[0:i], origin[i+1 : len(origin)]} + w := wildcard{origin[0:i], origin[i+1:]} c.allowedWOrigins = append(c.allowedWOrigins, w) } else { c.allowedOrigins = append(c.allowedOrigins, origin) @@ -139,7 +145,7 @@ func New(options Options) *Cors { // Allowed Headers if len(options.AllowedHeaders) == 0 { // Use sensible defaults - c.allowedHeaders = []string{"Origin", "Accept", "Content-Type"} + c.allowedHeaders = []string{"Origin", "Accept", "Content-Type", "X-Requested-With"} } else { // Origin is always appended as some browsers will always request for this header at preflight c.allowedHeaders = convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey) @@ -155,7 +161,7 @@ func New(options Options) *Cors { // Allowed Methods if len(options.AllowedMethods) == 0 { // Default is spec's "simple" methods - c.allowedMethods = []string{"GET", "POST"} + c.allowedMethods = []string{"GET", "POST", "HEAD"} } else { c.allowedMethods = convert(options.AllowedMethods, strings.ToUpper) } @@ -163,16 +169,27 @@ func New(options Options) *Cors { return c } -// Default creates a new Cors handler with default options +// Default creates a new Cors handler with default options. func Default() *Cors { return New(Options{}) } +// AllowAll create a new Cors handler with permissive configuration allowing all +// origins with all standard methods with any header and credentials. +func AllowAll() *Cors { + return New(Options{ + AllowedOrigins: []string{"*"}, + AllowedMethods: []string{"HEAD", "GET", "POST", "PUT", "PATCH", "DELETE"}, + AllowedHeaders: []string{"*"}, + AllowCredentials: false, + }) +} + // Handler apply the CORS specification on the request, and add relevant CORS headers // as necessary. func (c *Cors) Handler(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == "OPTIONS" { + if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" { c.logf("Handler: Preflight request") c.handlePreflight(w, r) // Preflight requests are standalone and should stop the chain as some other @@ -192,32 +209,9 @@ func (c *Cors) Handler(h http.Handler) http.Handler { }) } -// HandlerC is net/context aware handler -func (c *Cors) HandlerC(h xhandler.HandlerC) xhandler.HandlerC { - return xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { - if r.Method == "OPTIONS" { - c.logf("Handler: Preflight request") - c.handlePreflight(w, r) - // Preflight requests are standalone and should stop the chain as some other - // middleware may not handle OPTIONS requests correctly. One typical example - // is authentication middleware ; OPTIONS requests won't carry authentication - // headers (see #1) - if c.optionPassthrough { - h.ServeHTTPC(ctx, w, r) - } else { - w.WriteHeader(http.StatusOK) - } - } else { - c.logf("Handler: Actual request") - c.handleActualRequest(w, r) - h.ServeHTTPC(ctx, w, r) - } - }) -} - // HandlerFunc provides Martini compatible handler func (c *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) { - if r.Method == "OPTIONS" { + if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" { c.logf("HandlerFunc: Preflight request") c.handlePreflight(w, r) } else { @@ -228,7 +222,7 @@ func (c *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) { // Negroni compatible interface func (c *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { - if r.Method == "OPTIONS" { + if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" { c.logf("ServeHTTP: Preflight request") c.handlePreflight(w, r) // Preflight requests are standalone and should stop the chain as some other @@ -252,7 +246,7 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { headers := w.Header() origin := r.Header.Get("Origin") - if r.Method != "OPTIONS" { + if r.Method != http.MethodOptions { c.logf(" Preflight aborted: %s!=OPTIONS", r.Method) return } @@ -267,7 +261,7 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { c.logf(" Preflight aborted: empty origin") return } - if !c.isOriginAllowed(origin) { + if !c.isOriginAllowed(r, origin) { c.logf(" Preflight aborted: origin '%s' not allowed", origin) return } @@ -282,7 +276,11 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { c.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders) return } - headers.Set("Access-Control-Allow-Origin", origin) + if c.allowedOriginsAll { + headers.Set("Access-Control-Allow-Origin", "*") + } else { + headers.Set("Access-Control-Allow-Origin", origin) + } // Spec says: Since the list of methods can be unbounded, simply returning the method indicated // by Access-Control-Request-Method (if supported) can be enough headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod)) @@ -306,7 +304,7 @@ func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) { headers := w.Header() origin := r.Header.Get("Origin") - if r.Method == "OPTIONS" { + if r.Method == http.MethodOptions { c.logf(" Actual request no headers added: method == %s", r.Method) return } @@ -316,7 +314,7 @@ func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) { c.logf(" Actual request no headers added: missing origin") return } - if !c.isOriginAllowed(origin) { + if !c.isOriginAllowed(r, origin) { c.logf(" Actual request no headers added: origin '%s' not allowed", origin) return } @@ -330,7 +328,11 @@ func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) { return } - headers.Set("Access-Control-Allow-Origin", origin) + if c.allowedOriginsAll { + headers.Set("Access-Control-Allow-Origin", "*") + } else { + headers.Set("Access-Control-Allow-Origin", origin) + } if len(c.exposedHeaders) > 0 { headers.Set("Access-Control-Expose-Headers", strings.Join(c.exposedHeaders, ", ")) } @@ -349,7 +351,10 @@ func (c *Cors) logf(format string, a ...interface{}) { // isOriginAllowed checks if a given origin is allowed to perform cross-domain requests // on the endpoint -func (c *Cors) isOriginAllowed(origin string) bool { +func (c *Cors) isOriginAllowed(r *http.Request, origin string) bool { + if c.allowOriginRequestFunc != nil { + return c.allowOriginRequestFunc(r, origin) + } if c.allowOriginFunc != nil { return c.allowOriginFunc(origin) } @@ -378,7 +383,7 @@ func (c *Cors) isMethodAllowed(method string) bool { return false } method = strings.ToUpper(method) - if method == "OPTIONS" { + if method == http.MethodOptions { // Always allow preflight requests return true } diff --git a/vendor/github.com/rs/cors/go.mod b/vendor/github.com/rs/cors/go.mod new file mode 100644 index 000000000..0a4c65210 --- /dev/null +++ b/vendor/github.com/rs/cors/go.mod @@ -0,0 +1 @@ +module github.com/rs/cors diff --git a/vendor/github.com/rs/cors/utils.go b/vendor/github.com/rs/cors/utils.go index c7a0aa060..53ad9e9db 100644 --- a/vendor/github.com/rs/cors/utils.go +++ b/vendor/github.com/rs/cors/utils.go @@ -39,19 +39,20 @@ func parseHeaderList(headerList string) []string { headers := make([]string, 0, t) for i := 0; i < l; i++ { b := headerList[i] - if b >= 'a' && b <= 'z' { + switch { + case b >= 'a' && b <= 'z': if upper { h = append(h, b-toLower) } else { h = append(h, b) } - } else if b >= 'A' && b <= 'Z' { + case b >= 'A' && b <= 'Z': if !upper { h = append(h, b+toLower) } else { h = append(h, b) } - } else if b == '-' || b == '_' || (b >= '0' && b <= '9') { + case b == '-' || b == '_' || (b >= '0' && b <= '9'): h = append(h, b) } diff --git a/vendor/github.com/rs/xhandler/.travis.yml b/vendor/github.com/rs/xhandler/.travis.yml deleted file mode 100644 index b65c7a9f1..000000000 --- a/vendor/github.com/rs/xhandler/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: go -go: -- 1.5 -- tip -matrix: - allow_failures: - - go: tip diff --git a/vendor/github.com/rs/xhandler/LICENSE b/vendor/github.com/rs/xhandler/LICENSE deleted file mode 100644 index 47c5e9d2d..000000000 --- a/vendor/github.com/rs/xhandler/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2015 Olivier Poitrey - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/rs/xhandler/README.md b/vendor/github.com/rs/xhandler/README.md deleted file mode 100644 index 91c594bd2..000000000 --- a/vendor/github.com/rs/xhandler/README.md +++ /dev/null @@ -1,134 +0,0 @@ -# XHandler - -[![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/xhandler) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/xhandler/master/LICENSE) [![Build Status](https://travis-ci.org/rs/xhandler.svg?branch=master)](https://travis-ci.org/rs/xhandler) [![Coverage](http://gocover.io/_badge/github.com/rs/xhandler)](http://gocover.io/github.com/rs/xhandler) - -XHandler is a bridge between [net/context](https://godoc.org/golang.org/x/net/context) and `http.Handler`. - -It lets you enforce `net/context` in your handlers without sacrificing compatibility with existing `http.Handlers` nor imposing a specific router. - -Thanks to `net/context` deadline management, `xhandler` is able to enforce a per request deadline and will cancel the context when the client closes the connection unexpectedly. - -You may create your own `net/context` aware handler pretty much the same way as you would do with http.Handler. - -Read more about xhandler on [Dailymotion engineering blog](http://engineering.dailymotion.com/our-way-to-go/). - -## Installing - - go get -u github.com/rs/xhandler - -## Usage - -```go -package main - -import ( - "log" - "net/http" - "time" - - "github.com/rs/cors" - "github.com/rs/xhandler" - "golang.org/x/net/context" -) - -type myMiddleware struct { - next xhandler.HandlerC -} - -func (h myMiddleware) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request) { - ctx = context.WithValue(ctx, "test", "World") - h.next.ServeHTTPC(ctx, w, r) -} - -func main() { - c := xhandler.Chain{} - - // Add close notifier handler so context is cancelled when the client closes - // the connection - c.UseC(xhandler.CloseHandler) - - // Add timeout handler - c.UseC(xhandler.TimeoutHandler(2 * time.Second)) - - // Middleware putting something in the context - c.UseC(func(next xhandler.HandlerC) xhandler.HandlerC { - return myMiddleware{next: next} - }) - - // Mix it with a non-context-aware middleware handler - c.Use(cors.Default().Handler) - - // Final handler (using handlerFuncC), reading from the context - xh := xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { - value := ctx.Value("test").(string) - w.Write([]byte("Hello " + value)) - }) - - // Bridge context aware handlers with http.Handler using xhandler.Handle() - http.Handle("/test", c.Handler(xh)) - - if err := http.ListenAndServe(":8080", nil); err != nil { - log.Fatal(err) - } -} -``` - -### Using xmux - -Xhandler comes with an optional context aware [muxer](https://github.com/rs/xmux) forked from [httprouter](https://github.com/julienschmidt/httprouter): - -```go -package main - -import ( - "fmt" - "log" - "net/http" - "time" - - "github.com/rs/xhandler" - "github.com/rs/xmux" - "golang.org/x/net/context" -) - -func main() { - c := xhandler.Chain{} - - // Append a context-aware middleware handler - c.UseC(xhandler.CloseHandler) - - // Another context-aware middleware handler - c.UseC(xhandler.TimeoutHandler(2 * time.Second)) - - mux := xmux.New() - - // Use c.Handler to terminate the chain with your final handler - mux.GET("/welcome/:name", xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, req *http.Request) { - fmt.Fprintf(w, "Welcome %s!", xmux.Params(ctx).Get("name")) - })) - - if err := http.ListenAndServe(":8080", c.Handler(mux)); err != nil { - log.Fatal(err) - } -} -``` - -See [xmux](https://github.com/rs/xmux) for more examples. - -## Context Aware Middleware - -Here is a list of `net/context` aware middleware handlers implementing `xhandler.HandlerC` interface. - -Feel free to put up a PR linking your middleware if you have built one: - -| Middleware | Author | Description | -| ---------- | ------ | ----------- | -| [xmux](https://github.com/rs/xmux) | [Olivier Poitrey](https://github.com/rs) | HTTP request muxer | -| [xlog](https://github.com/rs/xlog) | [Olivier Poitrey](https://github.com/rs) | HTTP handler logger | -| [xstats](https://github.com/rs/xstats) | [Olivier Poitrey](https://github.com/rs) | A generic client for service instrumentation | -| [xaccess](https://github.com/rs/xaccess) | [Olivier Poitrey](https://github.com/rs) | HTTP handler access logger with [xlog](https://github.com/rs/xlog) and [xstats](https://github.com/rs/xstats) | -| [cors](https://github.com/rs/cors) | [Olivier Poitrey](https://github.com/rs) | [Cross Origin Resource Sharing](http://www.w3.org/TR/cors/) (CORS) support | - -## Licenses - -All source code is licensed under the [MIT License](https://raw.github.com/rs/xhandler/master/LICENSE). diff --git a/vendor/github.com/rs/xhandler/chain.go b/vendor/github.com/rs/xhandler/chain.go deleted file mode 100644 index 3e4bd359c..000000000 --- a/vendor/github.com/rs/xhandler/chain.go +++ /dev/null @@ -1,121 +0,0 @@ -package xhandler - -import ( - "net/http" - - "golang.org/x/net/context" -) - -// Chain is a helper for chaining middleware handlers together for easier -// management. -type Chain []func(next HandlerC) HandlerC - -// Add appends a variable number of additional middleware handlers -// to the middleware chain. Middleware handlers can either be -// context-aware or non-context aware handlers with the appropriate -// function signatures. -func (c *Chain) Add(f ...interface{}) { - for _, h := range f { - switch v := h.(type) { - case func(http.Handler) http.Handler: - c.Use(v) - case func(HandlerC) HandlerC: - c.UseC(v) - default: - panic("Adding invalid handler to the middleware chain") - } - } -} - -// With creates a new middleware chain from an existing chain, -// extending it with additional middleware. Middleware handlers -// can either be context-aware or non-context aware handlers -// with the appropriate function signatures. -func (c *Chain) With(f ...interface{}) *Chain { - n := make(Chain, len(*c)) - copy(n, *c) - n.Add(f...) - return &n -} - -// UseC appends a context-aware handler to the middleware chain. -func (c *Chain) UseC(f func(next HandlerC) HandlerC) { - *c = append(*c, f) -} - -// Use appends a standard http.Handler to the middleware chain without -// losing track of the context when inserted between two context aware handlers. -// -// Caveat: the f function will be called on each request so you are better off putting -// any initialization sequence outside of this function. -func (c *Chain) Use(f func(next http.Handler) http.Handler) { - xf := func(next HandlerC) HandlerC { - return HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { - n := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - next.ServeHTTPC(ctx, w, r) - }) - f(n).ServeHTTP(w, r) - }) - } - *c = append(*c, xf) -} - -// Handler wraps the provided final handler with all the middleware appended to -// the chain and returns a new standard http.Handler instance. -// The context.Background() context is injected automatically. -func (c Chain) Handler(xh HandlerC) http.Handler { - ctx := context.Background() - return c.HandlerCtx(ctx, xh) -} - -// HandlerFC is a helper to provide a function (HandlerFuncC) to Handler(). -// -// HandlerFC is equivalent to: -// c.Handler(xhandler.HandlerFuncC(xhc)) -func (c Chain) HandlerFC(xhf HandlerFuncC) http.Handler { - ctx := context.Background() - return c.HandlerCtx(ctx, HandlerFuncC(xhf)) -} - -// HandlerH is a helper to provide a standard http handler (http.HandlerFunc) -// to Handler(). Your final handler won't have access to the context though. -func (c Chain) HandlerH(h http.Handler) http.Handler { - ctx := context.Background() - return c.HandlerCtx(ctx, HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { - h.ServeHTTP(w, r) - })) -} - -// HandlerF is a helper to provide a standard http handler function -// (http.HandlerFunc) to Handler(). Your final handler won't have access -// to the context though. -func (c Chain) HandlerF(hf http.HandlerFunc) http.Handler { - ctx := context.Background() - return c.HandlerCtx(ctx, HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { - hf(w, r) - })) -} - -// HandlerCtx wraps the provided final handler with all the middleware appended to -// the chain and returns a new standard http.Handler instance. -func (c Chain) HandlerCtx(ctx context.Context, xh HandlerC) http.Handler { - return New(ctx, c.HandlerC(xh)) -} - -// HandlerC wraps the provided final handler with all the middleware appended to -// the chain and returns a HandlerC instance. -func (c Chain) HandlerC(xh HandlerC) HandlerC { - for i := len(c) - 1; i >= 0; i-- { - xh = c[i](xh) - } - return xh -} - -// HandlerCF wraps the provided final handler func with all the middleware appended to -// the chain and returns a HandlerC instance. -// -// HandlerCF is equivalent to: -// c.HandlerC(xhandler.HandlerFuncC(xhc)) -func (c Chain) HandlerCF(xhc HandlerFuncC) HandlerC { - return c.HandlerC(HandlerFuncC(xhc)) -} diff --git a/vendor/github.com/rs/xhandler/middleware.go b/vendor/github.com/rs/xhandler/middleware.go deleted file mode 100644 index 7ad8fba62..000000000 --- a/vendor/github.com/rs/xhandler/middleware.go +++ /dev/null @@ -1,59 +0,0 @@ -package xhandler - -import ( - "net/http" - "time" - - "golang.org/x/net/context" -) - -// CloseHandler returns a Handler, cancelling the context when the client -// connection closes unexpectedly. -func CloseHandler(next HandlerC) HandlerC { - return HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { - // Cancel the context if the client closes the connection - if wcn, ok := w.(http.CloseNotifier); ok { - var cancel context.CancelFunc - ctx, cancel = context.WithCancel(ctx) - defer cancel() - - notify := wcn.CloseNotify() - go func() { - select { - case <-notify: - cancel() - case <-ctx.Done(): - } - }() - } - - next.ServeHTTPC(ctx, w, r) - }) -} - -// TimeoutHandler returns a Handler which adds a timeout to the context. -// -// Child handlers have the responsability of obeying the context deadline and to return -// an appropriate error (or not) response in case of timeout. -func TimeoutHandler(timeout time.Duration) func(next HandlerC) HandlerC { - return func(next HandlerC) HandlerC { - return HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { - ctx, _ = context.WithTimeout(ctx, timeout) - next.ServeHTTPC(ctx, w, r) - }) - } -} - -// If is a special handler that will skip insert the condNext handler only if a condition -// applies at runtime. -func If(cond func(ctx context.Context, w http.ResponseWriter, r *http.Request) bool, condNext func(next HandlerC) HandlerC) func(next HandlerC) HandlerC { - return func(next HandlerC) HandlerC { - return HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { - if cond(ctx, w, r) { - condNext(next).ServeHTTPC(ctx, w, r) - } else { - next.ServeHTTPC(ctx, w, r) - } - }) - } -} diff --git a/vendor/github.com/rs/xhandler/xhandler.go b/vendor/github.com/rs/xhandler/xhandler.go deleted file mode 100644 index bc832cb1f..000000000 --- a/vendor/github.com/rs/xhandler/xhandler.go +++ /dev/null @@ -1,42 +0,0 @@ -// Package xhandler provides a bridge between http.Handler and net/context. -// -// xhandler enforces net/context in your handlers without sacrificing -// compatibility with existing http.Handlers nor imposing a specific router. -// -// Thanks to net/context deadline management, xhandler is able to enforce -// a per request deadline and will cancel the context in when the client close -// the connection unexpectedly. -// -// You may create net/context aware middlewares pretty much the same way as -// you would with http.Handler. -package xhandler // import "github.com/rs/xhandler" - -import ( - "net/http" - - "golang.org/x/net/context" -) - -// HandlerC is a net/context aware http.Handler -type HandlerC interface { - ServeHTTPC(context.Context, http.ResponseWriter, *http.Request) -} - -// HandlerFuncC type is an adapter to allow the use of ordinary functions -// as an xhandler.Handler. If f is a function with the appropriate signature, -// xhandler.HandlerFuncC(f) is a xhandler.Handler object that calls f. -type HandlerFuncC func(context.Context, http.ResponseWriter, *http.Request) - -// ServeHTTPC calls f(ctx, w, r). -func (f HandlerFuncC) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request) { - f(ctx, w, r) -} - -// New creates a conventional http.Handler injecting the provided root -// context to sub handlers. This handler is used as a bridge between conventional -// http.Handler and context aware handlers. -func New(ctx context.Context, h HandlerC) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - h.ServeHTTPC(ctx, w, r) - }) -} diff --git a/vendor/github.com/status-im/status-protocol-go/.gitignore b/vendor/github.com/status-im/status-protocol-go/.gitignore new file mode 100644 index 000000000..0e30e7099 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/.gitignore @@ -0,0 +1,15 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Editors +.idea diff --git a/vendor/github.com/status-im/status-protocol-go/.golangci.yml b/vendor/github.com/status-im/status-protocol-go/.golangci.yml new file mode 100644 index 000000000..cdf512388 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/.golangci.yml @@ -0,0 +1,3 @@ +run: + modules-download-mode: vendor + deadline: 5m diff --git a/vendor/github.com/status-im/status-protocol-go/.travis.yml b/vendor/github.com/status-im/status-protocol-go/.travis.yml new file mode 100644 index 000000000..ed474cf67 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/.travis.yml @@ -0,0 +1,27 @@ +notifications: + email: false + +language: go + +install: true + +env: +- GO111MODULE=on + +before_script: +- make install-linter + +matrix: + include: + - go: "1.11.x" + env: GOFLAGS=-mod=vendor + script: + - make lint + # fails without -a + - go test -a ./... # make test + - go: "1.12.x" + env: GOFLAGS=-mod=vendor + script: + - make lint + # fails without -a + - go test -a ./... # make test diff --git a/vendor/github.com/status-im/status-protocol-go/LICENSE b/vendor/github.com/status-im/status-protocol-go/LICENSE new file mode 100644 index 000000000..a612ad981 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/vendor/github.com/status-im/status-protocol-go/Makefile b/vendor/github.com/status-im/status-protocol-go/Makefile new file mode 100644 index 000000000..4c2de76be --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/Makefile @@ -0,0 +1,43 @@ +GO111MODULE = on + +ENABLE_METRICS ?= true +BUILD_FLAGS ?= $(shell echo "-ldflags '\ + -X github.com/status-im/status-protocol-go/vendor/github.com/ethereum/go-ethereum/metrics.EnabledStr=$(ENABLE_METRICS)'") + +test: + go test ./... +.PHONY: test + +test-race: + go test -race ./... +.PHONY: test-race + +lint: + golangci-lint run -v +.PHONY: lint + +vendor: + go mod tidy + go mod vendor + modvendor -copy="**/*.c **/*.h" -v +.PHONY: vendor + +install-linter: + # install linter + curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.17.1 +.PHONY: install-linter + +install-dev: + # a tool to vendor non-go files + go get github.com/goware/modvendor@latest + + go get github.com/golang/mock/gomock@latest + go install github.com/golang/mock/mockgen + + go get github.com/kevinburke/go-bindata/go-bindata@v3.13.0 + go get github.com/golang/protobuf/protoc-gen-go@v1.3.1 +.PHONY: install-dev + +generate: + go generate ./... +.PHONY: generate diff --git a/vendor/github.com/status-im/status-protocol-go/README.md b/vendor/github.com/status-im/status-protocol-go/README.md new file mode 100644 index 000000000..01b37da63 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/README.md @@ -0,0 +1,5 @@ +# status-protocol-go + +This is the Status Protocol implementation in Go. + +TBD diff --git a/vendor/github.com/status-im/status-protocol-go/adapters.go b/vendor/github.com/status-im/status-protocol-go/adapters.go new file mode 100644 index 000000000..37f2feadb --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/adapters.go @@ -0,0 +1,492 @@ +package statusproto + +import ( + "context" + "crypto/ecdsa" + "time" + + "go.uber.org/zap" + + "github.com/status-im/status-protocol-go/encryption/sharedsecret" + "github.com/status-im/status-protocol-go/transport/whisper/filter" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/golang/protobuf/proto" + "github.com/pkg/errors" + whisper "github.com/status-im/whisper/whisperv6" + + "github.com/status-im/status-protocol-go/encryption" + "github.com/status-im/status-protocol-go/encryption/multidevice" + transport "github.com/status-im/status-protocol-go/transport/whisper" + protocol "github.com/status-im/status-protocol-go/v1" +) + +// Whisper message properties. +const ( + whisperTTL = 15 + whisperPoW = 0.002 + whisperPoWTime = 5 +) + +// whisperAdapter is a bridge between encryption and transport +// layers. +type whisperAdapter struct { + privateKey *ecdsa.PrivateKey + transport *transport.WhisperServiceTransport + protocol *encryption.Protocol + logger *zap.Logger + + featureFlags featureFlags +} + +func newWhisperAdapter( + pk *ecdsa.PrivateKey, + t *transport.WhisperServiceTransport, + p *encryption.Protocol, + featureFlags featureFlags, + logger *zap.Logger, +) *whisperAdapter { + if logger == nil { + logger = zap.NewNop() + } + + return &whisperAdapter{ + privateKey: pk, + transport: t, + protocol: p, + featureFlags: featureFlags, + logger: logger.With(zap.Namespace("whisperAdapter")), + } +} + +func (a *whisperAdapter) JoinPublic(chatID string) error { + return a.transport.JoinPublic(chatID) +} + +func (a *whisperAdapter) LeavePublic(chatID string) error { + return a.transport.LeavePublic(chatID) +} + +func (a *whisperAdapter) JoinPrivate(publicKey *ecdsa.PublicKey) error { + return a.transport.JoinPrivate(publicKey) +} + +func (a *whisperAdapter) LeavePrivate(publicKey *ecdsa.PublicKey) error { + return a.transport.LeavePrivate(publicKey) +} + +// RetrievePublicMessages retrieves the collected public messages. +// It implies joining a chat if it has not been joined yet. +func (a *whisperAdapter) RetrievePublicMessages(chatID string) ([]*protocol.Message, error) { + messages, err := a.transport.RetrievePublicMessages(chatID) + if err != nil { + return nil, err + } + + logger := a.logger.With(zap.String("site", "RetrievePublicMessages")) + + decodedMessages := make([]*protocol.Message, 0, len(messages)) + for _, item := range messages { + shhMessage := whisper.ToWhisperMessage(item) + + hlogger := logger.With(zap.Binary("hash", shhMessage.Hash)) + + hlogger.Debug("received a public message") + + statusMessage, err := a.decodeMessage(shhMessage) + if err != nil { + hlogger.Error("failed to decode message", zap.Error(err)) + continue + } + + switch m := statusMessage.Message.(type) { + case protocol.Message: + m.ID = statusMessage.ID + m.SigPubKey = statusMessage.SigPubKey + decodedMessages = append(decodedMessages, &m) + default: + hlogger.Error("skipped a public message of unsupported type") + } + } + return decodedMessages, nil +} + +// RetrievePrivateMessages retrieves the collected private messages. +// It implies joining a chat if it has not been joined yet. +func (a *whisperAdapter) RetrievePrivateMessages(publicKey *ecdsa.PublicKey) ([]*protocol.Message, error) { + messages, err := a.transport.RetrievePrivateMessages(publicKey) + if err != nil { + return nil, err + } + + logger := a.logger.With(zap.String("site", "RetrievePrivateMessages")) + + decodedMessages := make([]*protocol.Message, 0, len(messages)) + for _, item := range messages { + shhMessage := whisper.ToWhisperMessage(item) + + hlogger := logger.With(zap.Binary("hash", shhMessage.Hash)) + + hlogger.Debug("received a private message") + + err := a.decryptMessage(context.Background(), shhMessage) + if err != nil { + hlogger.Error("failed to decrypt a message", zap.Error(err)) + } + + statusMessage, err := a.decodeMessage(shhMessage) + if err != nil { + hlogger.Error("failed to decode a message", zap.Error(err)) + continue + } + + switch m := statusMessage.Message.(type) { + case protocol.Message: + m.ID = statusMessage.ID + m.SigPubKey = statusMessage.SigPubKey + decodedMessages = append(decodedMessages, &m) + case protocol.PairMessage: + fromOurDevice := isPubKeyEqual(statusMessage.SigPubKey, &a.privateKey.PublicKey) + if !fromOurDevice { + hlogger.Debug("received PairMessage from not our device, skipping") + break + } + + metadata := &multidevice.InstallationMetadata{ + Name: m.Name, + FCMToken: m.FCMToken, + DeviceType: m.DeviceType, + } + err := a.protocol.SetInstallationMetadata(&a.privateKey.PublicKey, m.InstallationID, metadata) + if err != nil { + return nil, err + } + } + } + return decodedMessages, nil +} + +// DEPRECATED +func (a *whisperAdapter) RetrieveRawAll() (map[filter.Chat][]*whisper.Message, error) { + chatWithMessages, err := a.transport.RetrieveRawAll() + if err != nil { + return nil, err + } + + logger := a.logger.With(zap.String("site", "RetrieveRawAll")) + result := make(map[filter.Chat][]*whisper.Message) + + for chat, messages := range chatWithMessages { + for _, message := range messages { + shhMessage := whisper.ToWhisperMessage(message) + err := a.decryptMessage(context.Background(), shhMessage) + if err != nil { + logger.Warn("failed to decrypt a message", zap.Error(err), zap.Binary("messageID", shhMessage.Hash)) + } + result[chat] = append(result[chat], shhMessage) + } + } + + return result, nil +} + +// DEPRECATED +func (a *whisperAdapter) RetrieveRaw(filterID string) ([]*whisper.Message, error) { + messages, err := a.transport.RetrieveRaw(filterID) + if err != nil { + return nil, err + } + + logger := a.logger.With(zap.String("site", "RetrieveRaw")) + + var result []*whisper.Message + + for _, message := range messages { + shhMessage := whisper.ToWhisperMessage(message) + err := a.decryptMessage(context.Background(), shhMessage) + if err != nil { + logger.Warn("failed to decrypt a message", zap.Error(err), zap.Binary("messageID", shhMessage.Hash)) + } + result = append(result, shhMessage) + } + + return result, nil +} + +func (a *whisperAdapter) decodeMessage(message *whisper.Message) (*protocol.StatusMessage, error) { + + publicKey, err := crypto.UnmarshalPubkey(message.Sig) + if err != nil { + return nil, err + } + + decoded, err := protocol.DecodeMessage(publicKey, message.Payload) + if err != nil { + return nil, err + } + + return &decoded, nil +} + +func (a *whisperAdapter) decryptMessage(ctx context.Context, message *whisper.Message) error { + publicKey, err := crypto.UnmarshalPubkey(message.Sig) + if err != nil { + return errors.Wrap(err, "failed to get signature") + } + + var protocolMessage encryption.ProtocolMessage + + err = proto.Unmarshal(message.Payload, &protocolMessage) + if err != nil { + return errors.Wrap(err, "failed to unmarshal ProtocolMessage") + } + + logger := a.logger.With(zap.String("site", "decryptMessage")) + + payload, err := a.protocol.HandleMessage( + a.privateKey, + publicKey, + &protocolMessage, + message.Hash, + ) + if err == encryption.ErrDeviceNotFound { + handleErr := a.handleErrDeviceNotFound(ctx, publicKey) + if handleErr != nil { + logger.Error("failed to handle error", zap.Error(err), zap.NamedError("handleErr", handleErr)) + } + } + if err != nil { + return errors.Wrap(err, "failed to process an encrypted message") + } + + message.Payload = payload + return nil + +} + +func (a *whisperAdapter) handleErrDeviceNotFound(ctx context.Context, publicKey *ecdsa.PublicKey) error { + now := time.Now().Unix() + advertise, err := a.protocol.ShouldAdvertiseBundle(publicKey, now) + if err != nil { + return err + } + if !advertise { + return nil + } + + messageSpec, err := a.protocol.BuildBundleAdvertiseMessage(a.privateKey, publicKey) + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + _, err = a.sendMessageSpec(ctx, publicKey, messageSpec) + if err != nil { + return err + } + + a.protocol.ConfirmBundleAdvertisement(publicKey, now) + + return nil +} + +// SendPublic sends a public message passing chat name to the transport layer. +// +// Be aware that this method returns a message ID using protocol.MessageID +// instead of Whisper message hash. +func (a *whisperAdapter) SendPublic(ctx context.Context, chatName, chatID string, data []byte, clock int64) ([]byte, error) { + logger := a.logger.With(zap.String("site", "SendPublic")) + + logger.Debug("sending a public message", zap.String("chat-name", chatName)) + + message := protocol.CreatePublicTextMessage(data, clock, chatName) + + encodedMessage, err := a.encodeMessage(message) + if err != nil { + return nil, errors.Wrap(err, "failed to encode message") + } + + newMessage := whisper.NewMessage{ + TTL: whisperTTL, + Payload: encodedMessage, + PowTarget: whisperPoW, + PowTime: whisperPoWTime, + } + + _, err = a.transport.SendPublic(ctx, newMessage, chatName) + if err != nil { + return nil, err + } + + return protocol.MessageID(&a.privateKey.PublicKey, encodedMessage), nil +} + +// SendPublicRaw takes encoded data, encrypts it and sends through the wire. +// DEPRECATED +func (a *whisperAdapter) SendPublicRaw(ctx context.Context, chatName string, data []byte) ([]byte, whisper.NewMessage, error) { + newMessage := whisper.NewMessage{ + TTL: whisperTTL, + Payload: data, + PowTarget: whisperPoW, + PowTime: whisperPoWTime, + } + hash, err := a.transport.SendPublic(ctx, newMessage, chatName) + return hash, newMessage, err +} + +func (a *whisperAdapter) SendContactCode(ctx context.Context, messageSpec *encryption.ProtocolMessageSpec) ([]byte, error) { + newMessage, err := a.messageSpecToWhisper(messageSpec) + if err != nil { + return nil, err + } + + return a.transport.SendPublic(ctx, newMessage, filter.ContactCodeTopic(&a.privateKey.PublicKey)) +} + +func (a *whisperAdapter) encodeMessage(message protocol.Message) ([]byte, error) { + encodedMessage, err := protocol.EncodeMessage(message) + if err != nil { + return nil, errors.Wrap(err, "failed to encode message") + } + + if a.featureFlags.sendV1Messages { + encodedMessage, err = protocol.WrapMessageV1(encodedMessage, a.privateKey) + if err != nil { + return nil, errors.Wrap(err, "failed to wrap message") + } + + } + + return encodedMessage, nil +} + +// SendPrivate sends a one-to-one message. It needs to return it +// because the registered Whisper filter handles only incoming messages +// and our own messages need to be handled manually. +// +// This might be not true if a shared secret is used because it relies on +// symmetric encryption. +// +// Be aware that this method returns a message ID using protocol.MessageID +// instead of Whisper message hash. +func (a *whisperAdapter) SendPrivate( + ctx context.Context, + publicKey *ecdsa.PublicKey, + chatID string, + data []byte, + clock int64, +) ([]byte, *protocol.Message, error) { + logger := a.logger.With(zap.String("site", "SendPrivate")) + + logger.Debug("sending a private message", zap.Binary("public-key", crypto.FromECDSAPub(publicKey))) + + message := protocol.CreatePrivateTextMessage(data, clock, chatID) + + encodedMessage, err := a.encodeMessage(message) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to encode message") + } + + messageSpec, err := a.protocol.BuildDirectMessage(a.privateKey, publicKey, encodedMessage) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to encrypt message") + } + + _, err = a.sendMessageSpec(ctx, publicKey, messageSpec) + if err != nil { + return nil, nil, err + } + return protocol.MessageID(&a.privateKey.PublicKey, encodedMessage), &message, nil +} + +// SendPrivateRaw takes encoded data, encrypts it and sends through the wire. +// DEPRECATED +func (a *whisperAdapter) SendPrivateRaw( + ctx context.Context, + publicKey *ecdsa.PublicKey, + data []byte, +) ([]byte, whisper.NewMessage, error) { + a.logger.Debug( + "sending a private message", + zap.Binary("public-key", crypto.FromECDSAPub(publicKey)), + zap.String("site", "SendPrivateRaw"), + ) + + var newMessage whisper.NewMessage + + messageSpec, err := a.protocol.BuildDirectMessage(a.privateKey, publicKey, data) + if err != nil { + return nil, newMessage, errors.Wrap(err, "failed to encrypt message") + } + + newMessage, err = a.messageSpecToWhisper(messageSpec) + if err != nil { + return nil, newMessage, errors.Wrap(err, "failed to convert ProtocolMessageSpec to whisper.NewMessage") + } + + hash, err := a.sendMessageSpec(ctx, publicKey, messageSpec) + return hash, newMessage, err +} + +func (a *whisperAdapter) sendMessageSpec(ctx context.Context, publicKey *ecdsa.PublicKey, messageSpec *encryption.ProtocolMessageSpec) ([]byte, error) { + newMessage, err := a.messageSpecToWhisper(messageSpec) + if err != nil { + return nil, err + } + + logger := a.logger.With(zap.String("site", "sendMessageSpec")) + switch { + case messageSpec.SharedSecret != nil: + logger.Debug("sending using shared secret") + return a.transport.SendPrivateWithSharedSecret(ctx, newMessage, publicKey, messageSpec.SharedSecret) + case messageSpec.PartitionedTopicMode() == encryption.PartitionTopicV1: + logger.Debug("sending partitioned topic") + return a.transport.SendPrivateWithPartitioned(ctx, newMessage, publicKey) + case !a.featureFlags.genericDiscoveryTopicEnabled: + logger.Debug("sending partitioned topic (generic discovery topic disabled)") + return a.transport.SendPrivateWithPartitioned(ctx, newMessage, publicKey) + default: + logger.Debug("sending using discovery topic") + return a.transport.SendPrivateOnDiscovery(ctx, newMessage, publicKey) + } +} + +func (a *whisperAdapter) messageSpecToWhisper(spec *encryption.ProtocolMessageSpec) (whisper.NewMessage, error) { + var newMessage whisper.NewMessage + + payload, err := proto.Marshal(spec.Message) + if err != nil { + return newMessage, err + } + + newMessage = whisper.NewMessage{ + TTL: whisperTTL, + Payload: payload, + PowTarget: whisperPoW, + PowTime: whisperPoWTime, + } + return newMessage, nil +} + +func (a *whisperAdapter) handleSharedSecrets(secrets []*sharedsecret.Secret) error { + logger := a.logger.With(zap.String("site", "handleSharedSecrets")) + for _, secret := range secrets { + logger.Debug("received shared secret", zap.Binary("identity", crypto.FromECDSAPub(secret.Identity))) + + fSecret := filter.NegotiatedSecret{ + PublicKey: secret.Identity, + Key: secret.Key, + } + if err := a.transport.ProcessNegotiatedSecret(fSecret); err != nil { + return err + } + } + return nil +} + +// isPubKeyEqual checks that two public keys are equal +func isPubKeyEqual(a, b *ecdsa.PublicKey) bool { + // the curve is always the same, just compare the points + return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 +} diff --git a/vendor/github.com/status-im/status-protocol-go/chat.go b/vendor/github.com/status-im/status-protocol-go/chat.go new file mode 100644 index 000000000..f3f610ca4 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/chat.go @@ -0,0 +1,9 @@ +package statusproto + +import "crypto/ecdsa" + +type Chat interface { + ID() string + PublicName() string + PublicKey() *ecdsa.PublicKey +} diff --git a/messaging/crypto/crypto.go b/vendor/github.com/status-im/status-protocol-go/crypto/crypto.go similarity index 100% rename from messaging/crypto/crypto.go rename to vendor/github.com/status-im/status-protocol-go/crypto/crypto.go diff --git a/vendor/github.com/status-im/status-protocol-go/crypto/ethereum_crypto.go b/vendor/github.com/status-im/status-protocol-go/crypto/ethereum_crypto.go new file mode 100644 index 000000000..bf316d829 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/crypto/ethereum_crypto.go @@ -0,0 +1,190 @@ +package crypto + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/sha256" + "fmt" + "io" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/ecies" + dr "github.com/status-im/doubleratchet" + "golang.org/x/crypto/hkdf" +) + +// EthereumCrypto is an implementation of Crypto with cryptographic primitives recommended +// by the Double Ratchet Algorithm specification. However, some details are different, +// see function comments for details. +type EthereumCrypto struct{} + +// See the Crypto interface. +func (c EthereumCrypto) GenerateDH() (dr.DHPair, error) { + keys, err := crypto.GenerateKey() + if err != nil { + return nil, err + } + + var publicKey [32]byte + copy(publicKey[:], crypto.CompressPubkey(&keys.PublicKey)[:32]) + + var privateKey [32]byte + copy(privateKey[:], crypto.FromECDSA(keys)) + + return DHPair{ + PrvKey: privateKey, + PubKey: publicKey, + }, nil + +} + +// See the Crypto interface. +func (c EthereumCrypto) DH(dhPair dr.DHPair, dhPub dr.Key) dr.Key { + tmpKey := dhPair.PrivateKey() + privateKey, err := crypto.ToECDSA(tmpKey[:]) + eciesPrivate := ecies.ImportECDSA(privateKey) + var a [32]byte + if err != nil { + return a + } + + publicKey, err := crypto.DecompressPubkey(dhPub[:]) + if err != nil { + return a + } + eciesPublic := ecies.ImportECDSAPublic(publicKey) + + key, err := eciesPrivate.GenerateShared( + eciesPublic, + 16, + 16, + ) + + if err != nil { + return a + } + + copy(a[:], key) + return a + +} + +// See the Crypto interface. +func (c EthereumCrypto) KdfRK(rk, dhOut dr.Key) (rootKey, chainKey, headerKey dr.Key) { + var ( + // We can use a non-secret constant as the last argument + r = hkdf.New(sha256.New, dhOut[:], rk[:], []byte("rsZUpEuXUqqwXBvSy3EcievAh4cMj6QL")) + buf = make([]byte, 96) + ) + + // The only error here is an entropy limit which won't be reached for such a short buffer. + _, _ = io.ReadFull(r, buf) + + copy(rootKey[:], buf[:32]) + copy(chainKey[:], buf[32:64]) + copy(headerKey[:], buf[64:96]) + return +} + +// See the Crypto interface. +func (c EthereumCrypto) KdfCK(ck dr.Key) (chainKey dr.Key, msgKey dr.Key) { + const ( + ckInput = 15 + mkInput = 16 + ) + + h := hmac.New(sha256.New, ck[:]) + + _, _ = h.Write([]byte{ckInput}) + copy(chainKey[:], h.Sum(nil)) + h.Reset() + + _, _ = h.Write([]byte{mkInput}) + copy(msgKey[:], h.Sum(nil)) + + return chainKey, msgKey +} + +// Encrypt uses a slightly different approach than in the algorithm specification: +// it uses AES-256-CTR instead of AES-256-CBC for security, ciphertext length and implementation +// complexity considerations. +func (c EthereumCrypto) Encrypt(mk dr.Key, plaintext, ad []byte) []byte { + encKey, authKey, iv := c.deriveEncKeys(mk) + + ciphertext := make([]byte, aes.BlockSize+len(plaintext)) + copy(ciphertext, iv[:]) + + var ( + block, _ = aes.NewCipher(encKey[:]) // No error will occur here as encKey is guaranteed to be 32 bytes. + stream = cipher.NewCTR(block, iv[:]) + ) + stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext) + + return append(ciphertext, c.computeSignature(authKey[:], ciphertext, ad)...) +} + +// See the Crypto interface. +func (c EthereumCrypto) Decrypt(mk dr.Key, authCiphertext, ad []byte) ([]byte, error) { + var ( + l = len(authCiphertext) + ciphertext = authCiphertext[:l-sha256.Size] + signature = authCiphertext[l-sha256.Size:] + ) + + // Check the signature. + encKey, authKey, _ := c.deriveEncKeys(mk) + + if s := c.computeSignature(authKey[:], ciphertext, ad); !bytes.Equal(s, signature) { + return nil, fmt.Errorf("invalid signature") + } + + // Decrypt. + var ( + block, _ = aes.NewCipher(encKey[:]) // No error will occur here as encKey is guaranteed to be 32 bytes. + stream = cipher.NewCTR(block, ciphertext[:aes.BlockSize]) + plaintext = make([]byte, len(ciphertext[aes.BlockSize:])) + ) + stream.XORKeyStream(plaintext, ciphertext[aes.BlockSize:]) + + return plaintext, nil +} + +// deriveEncKeys derive keys for message encryption and decryption. Returns (encKey, authKey, iv, err). +func (c EthereumCrypto) deriveEncKeys(mk dr.Key) (encKey dr.Key, authKey dr.Key, iv [16]byte) { + // First, derive encryption and authentication key out of mk. + salt := make([]byte, 32) + var ( + r = hkdf.New(sha256.New, mk[:], salt, []byte("pcwSByyx2CRdryCffXJwy7xgVZWtW5Sh")) + buf = make([]byte, 80) + ) + + // The only error here is an entropy limit which won't be reached for such a short buffer. + _, _ = io.ReadFull(r, buf) + + copy(encKey[:], buf[0:32]) + copy(authKey[:], buf[32:64]) + copy(iv[:], buf[64:80]) + return +} + +func (c EthereumCrypto) computeSignature(authKey, ciphertext, associatedData []byte) []byte { + h := hmac.New(sha256.New, authKey) + _, _ = h.Write(associatedData) + _, _ = h.Write(ciphertext) + return h.Sum(nil) +} + +type DHPair struct { + PrvKey dr.Key + PubKey dr.Key +} + +func (p DHPair) PrivateKey() dr.Key { + return p.PrvKey +} + +func (p DHPair) PublicKey() dr.Key { + return p.PubKey +} diff --git a/messaging/chat/encryption.go b/vendor/github.com/status-im/status-protocol-go/encryption/encryptor.go similarity index 66% rename from messaging/chat/encryption.go rename to vendor/github.com/status-im/status-protocol-go/encryption/encryptor.go index b5db8a746..db9b00651 100644 --- a/messaging/chat/encryption.go +++ b/vendor/github.com/status-im/status-protocol-go/encryption/encryptor.go @@ -1,24 +1,26 @@ -package chat +package encryption import ( "crypto/ecdsa" "encoding/hex" "errors" + "fmt" "sync" "time" ecrypto "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/ecies" - "github.com/ethereum/go-ethereum/log" dr "github.com/status-im/doubleratchet" + "go.uber.org/zap" - "github.com/status-im/status-go/messaging/chat/protobuf" - "github.com/status-im/status-go/messaging/crypto" - "github.com/status-im/status-go/messaging/multidevice" + "github.com/status-im/status-protocol-go/crypto" + migrations "github.com/status-im/status-protocol-go/encryption/internal/sqlite" + "github.com/status-im/status-protocol-go/encryption/multidevice" + "github.com/status-im/status-protocol-go/sqlite" ) var ( - ErrSessionNotFound = errors.New("session not found") + errSessionNotFound = errors.New("session not found") ErrDeviceNotFound = errors.New("device not found") // ErrNotPairedDevice means that we received a message signed with our public key // but from a device that has not been paired. @@ -30,21 +32,21 @@ var ( // If we have no bundles, we use a constant so that the message can reach any device. const noInstallationID = "none" -type ConfirmationData struct { +type confirmationData struct { header *dr.MessageHeader drInfo *RatchetInfo } -// EncryptionService defines a service that is responsible for the encryption aspect of the protocol. -type EncryptionService struct { - log log.Logger - persistence Persistence - config EncryptionServiceConfig - messageIDs map[string]*ConfirmationData +// encryptor defines a service that is responsible for the encryption aspect of the protocol. +type encryptor struct { + persistence *sqlitePersistence + config encryptorConfig + messageIDs map[string]*confirmationData mutex sync.Mutex + logger *zap.Logger } -type EncryptionServiceConfig struct { +type encryptorConfig struct { InstallationID string // Max number of installations we keep synchronized. MaxInstallations int @@ -56,34 +58,48 @@ type EncryptionServiceConfig struct { MaxMessageKeysPerSession int // How long before we refresh the interval in milliseconds BundleRefreshInterval int64 + // The logging object + Logger *zap.Logger } -// DefaultEncryptionServiceConfig returns the default values used by the encryption service -func DefaultEncryptionServiceConfig(installationID string) EncryptionServiceConfig { - return EncryptionServiceConfig{ +// defaultEncryptorConfig returns the default values used by the encryption service +func defaultEncryptorConfig(installationID string, logger *zap.Logger) encryptorConfig { + if logger == nil { + logger = zap.NewNop() + } + + return encryptorConfig{ MaxInstallations: 3, MaxSkip: 1000, MaxKeep: 3000, MaxMessageKeysPerSession: 2000, BundleRefreshInterval: 24 * 60 * 60 * 1000, InstallationID: installationID, + Logger: logger, } } -// NewEncryptionService creates a new EncryptionService instance. -func NewEncryptionService(p Persistence, config EncryptionServiceConfig) *EncryptionService { - logger := log.New("package", "status-go/services/sshext.chat") - logger.Info("Initialized encryption service", "installationID", config.InstallationID) - return &EncryptionService{ - log: logger, - persistence: p, +// newEncryptor creates a new EncryptionService instance. +func newEncryptor(dbPath, dbKey string, config encryptorConfig) (*encryptor, error) { + db, err := sqlite.Open(dbPath, dbKey, sqlite.MigrationConfig{ + AssetNames: migrations.AssetNames(), + AssetGetter: func(name string) ([]byte, error) { + return migrations.Asset(name) + }, + }) + if err != nil { + return nil, err + } + + return &encryptor{ + persistence: newSQLitePersistence(db), config: config, - mutex: sync.Mutex{}, - messageIDs: make(map[string]*ConfirmationData), - } + messageIDs: make(map[string]*confirmationData), + logger: config.Logger.With(zap.Namespace("encryptor")), + }, nil } -func (s *EncryptionService) keyFromActiveX3DH(theirIdentityKey []byte, theirSignedPreKey []byte, myIdentityKey *ecdsa.PrivateKey) ([]byte, *ecdsa.PublicKey, error) { +func (s *encryptor) keyFromActiveX3DH(theirIdentityKey []byte, theirSignedPreKey []byte, myIdentityKey *ecdsa.PrivateKey) ([]byte, *ecdsa.PublicKey, error) { sharedKey, ephemeralPubKey, err := PerformActiveX3DH(theirIdentityKey, theirSignedPreKey, myIdentityKey) if err != nil { return nil, nil, err @@ -92,18 +108,17 @@ func (s *EncryptionService) keyFromActiveX3DH(theirIdentityKey []byte, theirSign return sharedKey, ephemeralPubKey, nil } -func (s *EncryptionService) getDRSession(id []byte) (dr.Session, error) { - sessionStorage := s.persistence.GetSessionStorage() +func (s *encryptor) getDRSession(id []byte) (dr.Session, error) { + sessionStorage := s.persistence.SessionStorage() return dr.Load( id, sessionStorage, - dr.WithKeysStorage(s.persistence.GetKeysStorage()), + dr.WithKeysStorage(s.persistence.KeysStorage()), dr.WithMaxSkip(s.config.MaxSkip), dr.WithMaxKeep(s.config.MaxKeep), dr.WithMaxMessageKeysPerSession(s.config.MaxMessageKeysPerSession), dr.WithCrypto(crypto.EthereumCrypto{}), ) - } func confirmationIDString(id []byte) string { @@ -111,33 +126,32 @@ func confirmationIDString(id []byte) string { } // ConfirmMessagesProcessed confirms and deletes message keys for the given messages -func (s *EncryptionService) ConfirmMessagesProcessed(messageIDs [][]byte) error { +func (s *encryptor) ConfirmMessageProcessed(messageID []byte) error { s.mutex.Lock() defer s.mutex.Unlock() - for _, idByte := range messageIDs { - id := confirmationIDString(idByte) - confirmationData, ok := s.messageIDs[id] - if !ok { - s.log.Debug("Could not confirm message", "messageID", id) - continue - } - - // Load session from store first - session, err := s.getDRSession(confirmationData.drInfo.ID) - if err != nil { - return err - } - - if err := session.DeleteMk(confirmationData.header.DH, confirmationData.header.N); err != nil { - return err - } + id := confirmationIDString(messageID) + confirmationData, ok := s.messageIDs[id] + if !ok { + s.logger.Debug("could not confirm message", zap.String("messageID", id)) + return fmt.Errorf("message with ID %#x not found", messageID) } + + // Load session from store first + session, err := s.getDRSession(confirmationData.drInfo.ID) + if err != nil { + return err + } + + if err := session.DeleteMk(confirmationData.header.DH, confirmationData.header.N); err != nil { + return err + } + return nil } // CreateBundle retrieves or creates an X3DH bundle given a private key -func (s *EncryptionService) CreateBundle(privateKey *ecdsa.PrivateKey, installations []*multidevice.Installation) (*protobuf.Bundle, error) { +func (s *encryptor) CreateBundle(privateKey *ecdsa.PrivateKey, installations []*multidevice.Installation) (*Bundle, error) { ourIdentityKeyC := ecrypto.CompressPubkey(&privateKey.PublicKey) bundleContainer, err := s.persistence.GetAnyPrivateBundle(ourIdentityKeyC, installations) @@ -145,8 +159,10 @@ func (s *EncryptionService) CreateBundle(privateKey *ecdsa.PrivateKey, installat return nil, err } + expired := bundleContainer != nil && bundleContainer.GetBundle().Timestamp < time.Now().Add(-1*time.Duration(s.config.BundleRefreshInterval)*time.Millisecond).UnixNano() + // If the bundle has expired we create a new one - if bundleContainer != nil && bundleContainer.GetBundle().Timestamp < time.Now().Add(-1*time.Duration(s.config.BundleRefreshInterval)*time.Millisecond).UnixNano() { + if expired { // Mark sessions has expired if err := s.persistence.MarkBundleExpired(bundleContainer.GetBundle().GetIdentity()); err != nil { return nil, err @@ -175,7 +191,7 @@ func (s *EncryptionService) CreateBundle(privateKey *ecdsa.PrivateKey, installat } // DecryptWithDH decrypts message sent with a DH key exchange, and throws away the key after decryption -func (s *EncryptionService) DecryptWithDH(myIdentityKey *ecdsa.PrivateKey, theirEphemeralKey *ecdsa.PublicKey, payload []byte) ([]byte, error) { +func (s *encryptor) DecryptWithDH(myIdentityKey *ecdsa.PrivateKey, theirEphemeralKey *ecdsa.PublicKey, payload []byte) ([]byte, error) { key, err := PerformDH( ecies.ImportECDSA(myIdentityKey), ecies.ImportECDSAPublic(theirEphemeralKey), @@ -189,20 +205,20 @@ func (s *EncryptionService) DecryptWithDH(myIdentityKey *ecdsa.PrivateKey, their } // keyFromPassiveX3DH decrypts message sent with a X3DH key exchange, storing the key for future exchanges -func (s *EncryptionService) keyFromPassiveX3DH(myIdentityKey *ecdsa.PrivateKey, theirIdentityKey *ecdsa.PublicKey, theirEphemeralKey *ecdsa.PublicKey, ourBundleID []byte) ([]byte, error) { +func (s *encryptor) keyFromPassiveX3DH(myIdentityKey *ecdsa.PrivateKey, theirIdentityKey *ecdsa.PublicKey, theirEphemeralKey *ecdsa.PublicKey, ourBundleID []byte) ([]byte, error) { bundlePrivateKey, err := s.persistence.GetPrivateKeyBundle(ourBundleID) if err != nil { - s.log.Error("Could not get private bundle", "err", err) + s.logger.Error("could not get private bundle", zap.Error(err)) return nil, err } if bundlePrivateKey == nil { - return nil, ErrSessionNotFound + return nil, errSessionNotFound } signedPreKey, err := ecrypto.ToECDSA(bundlePrivateKey) if err != nil { - s.log.Error("Could not convert to ecdsa", "err", err) + s.logger.Error("could not convert to ecdsa", zap.Error(err)) return nil, err } @@ -213,19 +229,19 @@ func (s *EncryptionService) keyFromPassiveX3DH(myIdentityKey *ecdsa.PrivateKey, myIdentityKey, ) if err != nil { - s.log.Error("Could not perform passive x3dh", "err", err) + s.logger.Error("could not perform passive x3dh", zap.Error(err)) return nil, err } return key, nil } // ProcessPublicBundle persists a bundle -func (s *EncryptionService) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, b *protobuf.Bundle) error { +func (s *encryptor) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, b *Bundle) error { return s.persistence.AddPublicBundle(b) } // DecryptPayload decrypts the payload of a DirectMessageProtocol, given an identity private key and the sender's public key -func (s *EncryptionService) DecryptPayload(myIdentityKey *ecdsa.PrivateKey, theirIdentityKey *ecdsa.PublicKey, theirInstallationID string, msgs map[string]*protobuf.DirectMessageProtocol, messageID []byte) ([]byte, error) { +func (s *encryptor) DecryptPayload(myIdentityKey *ecdsa.PrivateKey, theirIdentityKey *ecdsa.PublicKey, theirInstallationID string, msgs map[string]*DirectMessageProtocol, messageID []byte) ([]byte, error) { s.mutex.Lock() defer s.mutex.Unlock() @@ -280,22 +296,22 @@ func (s *EncryptionService) DecryptPayload(myIdentityKey *ecdsa.PrivateKey, thei drInfo, err := s.persistence.GetRatchetInfo(drHeader.GetId(), theirIdentityKeyC, theirInstallationID) if err != nil { - s.log.Error("Could not get ratchet info", "err", err) + s.logger.Error("could not get ratchet info", zap.Error(err)) return nil, err } // We mark the exchange as successful so we stop sending x3dh header if err = s.persistence.RatchetInfoConfirmed(drHeader.GetId(), theirIdentityKeyC, theirInstallationID); err != nil { - s.log.Error("Could not confirm ratchet info", "err", err) + s.logger.Error("could not confirm ratchet info", zap.Error(err)) return nil, err } if drInfo == nil { - s.log.Error("Could not find a session") - return nil, ErrSessionNotFound + s.logger.Error("could not find a session") + return nil, errSessionNotFound } - confirmationData := &ConfirmationData{ + confirmationData := &confirmationData{ header: &drMessage.Header, drInfo: drInfo, } @@ -316,7 +332,7 @@ func (s *EncryptionService) DecryptPayload(myIdentityKey *ecdsa.PrivateKey, thei return nil, errors.New("no key specified") } -func (s *EncryptionService) createNewSession(drInfo *RatchetInfo, sk [32]byte, keyPair crypto.DHPair) (dr.Session, error) { +func (s *encryptor) createNewSession(drInfo *RatchetInfo, sk [32]byte, keyPair crypto.DHPair) (dr.Session, error) { var err error var session dr.Session @@ -325,8 +341,8 @@ func (s *EncryptionService) createNewSession(drInfo *RatchetInfo, sk [32]byte, k drInfo.ID, sk, keyPair, - s.persistence.GetSessionStorage(), - dr.WithKeysStorage(s.persistence.GetKeysStorage()), + s.persistence.SessionStorage(), + dr.WithKeysStorage(s.persistence.KeysStorage()), dr.WithMaxSkip(s.config.MaxSkip), dr.WithMaxKeep(s.config.MaxKeep), dr.WithMaxMessageKeysPerSession(s.config.MaxMessageKeysPerSession), @@ -336,8 +352,8 @@ func (s *EncryptionService) createNewSession(drInfo *RatchetInfo, sk [32]byte, k drInfo.ID, sk, keyPair.PubKey, - s.persistence.GetSessionStorage(), - dr.WithKeysStorage(s.persistence.GetKeysStorage()), + s.persistence.SessionStorage(), + dr.WithKeysStorage(s.persistence.KeysStorage()), dr.WithMaxSkip(s.config.MaxSkip), dr.WithMaxKeep(s.config.MaxKeep), dr.WithMaxMessageKeysPerSession(s.config.MaxMessageKeysPerSession), @@ -347,7 +363,7 @@ func (s *EncryptionService) createNewSession(drInfo *RatchetInfo, sk [32]byte, k return session, err } -func (s *EncryptionService) encryptUsingDR(theirIdentityKey *ecdsa.PublicKey, drInfo *RatchetInfo, payload []byte) ([]byte, *protobuf.DRHeader, error) { +func (s *encryptor) encryptUsingDR(theirIdentityKey *ecdsa.PublicKey, drInfo *RatchetInfo, payload []byte) ([]byte, *DRHeader, error) { var err error var session dr.Session @@ -381,7 +397,7 @@ func (s *EncryptionService) encryptUsingDR(theirIdentityKey *ecdsa.PublicKey, dr return nil, nil, err } - header := &protobuf.DRHeader{ + header := &DRHeader{ Id: drInfo.BundleID, Key: response.Header.DH[:], N: response.Header.N, @@ -391,7 +407,7 @@ func (s *EncryptionService) encryptUsingDR(theirIdentityKey *ecdsa.PublicKey, dr return response.Ciphertext, header, nil } -func (s *EncryptionService) decryptUsingDR(theirIdentityKey *ecdsa.PublicKey, drInfo *RatchetInfo, payload *dr.Message) ([]byte, error) { +func (s *encryptor) decryptUsingDR(theirIdentityKey *ecdsa.PublicKey, drInfo *RatchetInfo, payload *dr.Message) ([]byte, error) { var err error var session dr.Session @@ -425,7 +441,7 @@ func (s *EncryptionService) decryptUsingDR(theirIdentityKey *ecdsa.PublicKey, dr return plaintext, nil } -func (s *EncryptionService) encryptWithDH(theirIdentityKey *ecdsa.PublicKey, payload []byte) (*protobuf.DirectMessageProtocol, error) { +func (s *encryptor) encryptWithDH(theirIdentityKey *ecdsa.PublicKey, payload []byte) (*DirectMessageProtocol, error) { symmetricKey, ourEphemeralKey, err := PerformActiveDH(theirIdentityKey) if err != nil { return nil, err @@ -436,16 +452,16 @@ func (s *EncryptionService) encryptWithDH(theirIdentityKey *ecdsa.PublicKey, pay return nil, err } - return &protobuf.DirectMessageProtocol{ - DHHeader: &protobuf.DHHeader{ + return &DirectMessageProtocol{ + DHHeader: &DHHeader{ Key: ecrypto.CompressPubkey(ourEphemeralKey), }, Payload: encryptedPayload, }, nil } -func (s *EncryptionService) EncryptPayloadWithDH(theirIdentityKey *ecdsa.PublicKey, payload []byte) (map[string]*protobuf.DirectMessageProtocol, error) { - response := make(map[string]*protobuf.DirectMessageProtocol) +func (s *encryptor) EncryptPayloadWithDH(theirIdentityKey *ecdsa.PublicKey, payload []byte) (map[string]*DirectMessageProtocol, error) { + response := make(map[string]*DirectMessageProtocol) dmp, err := s.encryptWithDH(theirIdentityKey, payload) if err != nil { return nil, err @@ -456,37 +472,41 @@ func (s *EncryptionService) EncryptPayloadWithDH(theirIdentityKey *ecdsa.PublicK } // GetPublicBundle returns the active installations bundles for a given user -func (s *EncryptionService) GetPublicBundle(theirIdentityKey *ecdsa.PublicKey, installations []*multidevice.Installation) (*protobuf.Bundle, error) { +func (s *encryptor) GetPublicBundle(theirIdentityKey *ecdsa.PublicKey, installations []*multidevice.Installation) (*Bundle, error) { return s.persistence.GetPublicBundle(theirIdentityKey, installations) } // EncryptPayload returns a new DirectMessageProtocol with a given payload encrypted, given a recipient's public key and the sender private identity key -func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, myIdentityKey *ecdsa.PrivateKey, installations []*multidevice.Installation, payload []byte) (map[string]*protobuf.DirectMessageProtocol, []*multidevice.Installation, error) { +func (s *encryptor) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, myIdentityKey *ecdsa.PrivateKey, installations []*multidevice.Installation, payload []byte) (map[string]*DirectMessageProtocol, []*multidevice.Installation, error) { + logger := s.logger.With( + zap.String("site", "EncryptPayload"), + zap.Binary("their-identity-key", ecrypto.FromECDSAPub(theirIdentityKey))) + + logger.Debug("encrypting payload") // Which installations we are sending the message to var targetedInstallations []*multidevice.Installation s.mutex.Lock() defer s.mutex.Unlock() - s.log.Debug("Sending message", "theirKey", theirIdentityKey) - - theirIdentityKeyC := ecrypto.CompressPubkey(theirIdentityKey) - // We don't have any, send a message with DH if len(installations) == 0 { - s.log.Debug("no installations, sending to all devices") + logger.Debug("no installations, sending to all devices") encryptedPayload, err := s.EncryptPayloadWithDH(theirIdentityKey, payload) return encryptedPayload, targetedInstallations, err } - response := make(map[string]*protobuf.DirectMessageProtocol) + theirIdentityKeyC := ecrypto.CompressPubkey(theirIdentityKey) + response := make(map[string]*DirectMessageProtocol) for _, installation := range installations { installationID := installation.ID - s.log.Debug("Processing installation", "installationID", installationID) + ilogger := logger.With(zap.String("installation-id", installationID)) + ilogger.Debug("processing installation") if s.config.InstallationID == installationID { continue } + bundle, err := s.persistence.GetPublicBundle(theirIdentityKey, []*multidevice.Installation{installation}) if err != nil { return nil, nil, err @@ -501,19 +521,19 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my targetedInstallations = append(targetedInstallations, installation) if drInfo != nil { - s.log.Debug("Found DR info", "installationID", installationID) + ilogger.Debug("found DR info for installation") encryptedPayload, drHeader, err := s.encryptUsingDR(theirIdentityKey, drInfo, payload) if err != nil { return nil, nil, err } - dmp := protobuf.DirectMessageProtocol{ + dmp := DirectMessageProtocol{ Payload: encryptedPayload, DRHeader: drHeader, } if drInfo.EphemeralKey != nil { - dmp.X3DHHeader = &protobuf.X3DHHeader{ + dmp.X3DHHeader = &X3DHHeader{ Key: drInfo.EphemeralKey, Id: drInfo.BundleID, } @@ -527,11 +547,12 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my // This should not be nil at this point if theirSignedPreKeyContainer == nil { - s.log.Warn("Could not find either a ratchet info or a bundle for installationId", "installationID", installationID) + ilogger.Warn("could not find DR info or bundle for installation") continue } - s.log.Debug("DR info not found, using bundle", "installationID", installationID) + + ilogger.Debug("DR info not found, using bundle") theirSignedPreKey := theirSignedPreKeyContainer.GetSignedPreKey() @@ -547,7 +568,7 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my return nil, nil, err } - x3dhHeader := &protobuf.X3DHHeader{ + x3dhHeader := &X3DHHeader{ Key: ourEphemeralKeyC, Id: theirSignedPreKey, } @@ -563,7 +584,7 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my return nil, nil, err } - dmp := &protobuf.DirectMessageProtocol{ + dmp := &DirectMessageProtocol{ Payload: encryptedPayload, X3DHHeader: x3dhHeader, DRHeader: drHeader, @@ -573,7 +594,14 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my } } - s.log.Debug("Built message", "theirKey", theirIdentityKey) + var installationIDs []string + for _, i := range targetedInstallations { + installationIDs = append(installationIDs, i.ID) + } + logger.Info( + "built a message", + zap.Strings("installation-ids", installationIDs), + ) return response, targetedInstallations, nil } diff --git a/messaging/db/migrations/bindata.go b/vendor/github.com/status-im/status-protocol-go/encryption/internal/sqlite/migrations.go similarity index 65% rename from messaging/db/migrations/bindata.go rename to vendor/github.com/status-im/status-protocol-go/encryption/internal/sqlite/migrations.go index 0e9116d92..44856cdc6 100644 --- a/messaging/db/migrations/bindata.go +++ b/vendor/github.com/status-im/status-protocol-go/encryption/internal/sqlite/migrations.go @@ -1,30 +1,30 @@ -// Code generated by go-bindata. +// Code generated by go-bindata. DO NOT EDIT. // sources: -// 1536754952_initial_schema.down.sql -// 1536754952_initial_schema.up.sql -// 1539249977_update_ratchet_info.down.sql -// 1539249977_update_ratchet_info.up.sql -// 1540715431_add_version.down.sql -// 1540715431_add_version.up.sql -// 1541164797_add_installations.down.sql -// 1541164797_add_installations.up.sql -// 1558084410_add_secret.down.sql -// 1558084410_add_secret.up.sql -// 1558588866_add_version.up.sql -// 1559627659_add_contact_code.down.sql -// 1559627659_add_contact_code.up.sql -// 1561059285_add_whisper_keys.down.sql -// 1561059285_add_whisper_keys.up.sql -// 1561368210_add_installation_metadata.down.sql -// 1561368210_add_installation_metadata.up.sql -// static.go -// DO NOT EDIT! +// 1536754952_initial_schema.down.sql (83B) +// 1536754952_initial_schema.up.sql (962B) +// 1539249977_update_ratchet_info.down.sql (311B) +// 1539249977_update_ratchet_info.up.sql (368B) +// 1540715431_add_version.down.sql (127B) +// 1540715431_add_version.up.sql (265B) +// 1541164797_add_installations.down.sql (26B) +// 1541164797_add_installations.up.sql (216B) +// 1558084410_add_secret.down.sql (56B) +// 1558084410_add_secret.up.sql (301B) +// 1558588866_add_version.up.sql (57B) +// 1559627659_add_contact_code.down.sql (32B) +// 1559627659_add_contact_code.up.sql (198B) +// 1561059285_add_whisper_keys.down.sql (25B) +// 1561059285_add_whisper_keys.up.sql (112B) +// 1561368210_add_installation_metadata.down.sql (35B) +// 1561368210_add_installation_metadata.up.sql (267B) +// doc.go (377B) -package migrations +package sqlite import ( "bytes" "compress/gzip" + "crypto/sha256" "fmt" "io" "io/ioutil" @@ -37,7 +37,7 @@ import ( func bindataRead(data []byte, name string) ([]byte, error) { gz, err := gzip.NewReader(bytes.NewBuffer(data)) if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) + return nil, fmt.Errorf("read %q: %v", name, err) } var buf bytes.Buffer @@ -45,7 +45,7 @@ func bindataRead(data []byte, name string) ([]byte, error) { clErr := gz.Close() if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) + return nil, fmt.Errorf("read %q: %v", name, err) } if clErr != nil { return nil, err @@ -55,8 +55,9 @@ func bindataRead(data []byte, name string) ([]byte, error) { } type asset struct { - bytes []byte - info os.FileInfo + bytes []byte + info os.FileInfo + digest [sha256.Size]byte } type bindataFileInfo struct { @@ -100,8 +101,8 @@ func _1536754952_initial_schemaDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1536754952_initial_schema.down.sql", size: 83, mode: os.FileMode(420), modTime: time.Unix(1561038914, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1536754952_initial_schema.down.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x44, 0xcf, 0x76, 0x71, 0x1f, 0x5e, 0x9a, 0x43, 0xd8, 0xcd, 0xb8, 0xc3, 0x70, 0xc3, 0x7f, 0xfc, 0x90, 0xb4, 0x25, 0x1e, 0xf4, 0x66, 0x20, 0xb8, 0x33, 0x7e, 0xb0, 0x76, 0x1f, 0xc, 0xc0, 0x75}} return a, nil } @@ -120,8 +121,8 @@ func _1536754952_initial_schemaUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1536754952_initial_schema.up.sql", size: 962, mode: os.FileMode(420), modTime: time.Unix(1561038914, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1536754952_initial_schema.up.sql", size: 962, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xea, 0x90, 0x5a, 0x59, 0x3e, 0x3, 0xe2, 0x3c, 0x81, 0x42, 0xcd, 0x4c, 0x9a, 0xe8, 0xda, 0x93, 0x2b, 0x70, 0xa4, 0xd5, 0x29, 0x3e, 0xd5, 0xc9, 0x27, 0xb6, 0xb7, 0x65, 0xff, 0x0, 0xcb, 0xde}} return a, nil } @@ -140,8 +141,8 @@ func _1539249977_update_ratchet_infoDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1539249977_update_ratchet_info.down.sql", size: 311, mode: os.FileMode(420), modTime: time.Unix(1561038914, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1539249977_update_ratchet_info.down.sql", size: 311, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1, 0xa4, 0xeb, 0xa0, 0xe6, 0xa0, 0xd4, 0x48, 0xbb, 0xad, 0x6f, 0x7d, 0x67, 0x8c, 0xbd, 0x25, 0xde, 0x1f, 0x73, 0x9a, 0xbb, 0xa8, 0xc9, 0x30, 0xb7, 0xa9, 0x7c, 0xaf, 0xb5, 0x1, 0x61, 0xdd}} return a, nil } @@ -160,8 +161,8 @@ func _1539249977_update_ratchet_infoUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1539249977_update_ratchet_info.up.sql", size: 368, mode: os.FileMode(420), modTime: time.Unix(1561038914, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1539249977_update_ratchet_info.up.sql", size: 368, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc, 0x8e, 0xbf, 0x6f, 0xa, 0xc0, 0xe1, 0x3c, 0x42, 0x28, 0x88, 0x1d, 0xdb, 0xba, 0x1c, 0x83, 0xec, 0xba, 0xd3, 0x5f, 0x5c, 0x77, 0x5e, 0xa7, 0x46, 0x36, 0xec, 0x69, 0xa, 0x4b, 0x17, 0x79}} return a, nil } @@ -180,8 +181,8 @@ func _1540715431_add_versionDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1540715431_add_version.down.sql", size: 127, mode: os.FileMode(420), modTime: time.Unix(1561038914, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1540715431_add_version.down.sql", size: 127, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0x9, 0x4, 0xe3, 0x76, 0x2e, 0xb8, 0x9, 0x23, 0xf0, 0x70, 0x93, 0xc4, 0x50, 0xe, 0x9d, 0x84, 0x22, 0x8c, 0x94, 0xd3, 0x24, 0x9, 0x9a, 0xc1, 0xa1, 0x48, 0x45, 0xfd, 0x40, 0x6e, 0xe6}} return a, nil } @@ -200,8 +201,8 @@ func _1540715431_add_versionUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1540715431_add_version.up.sql", size: 265, mode: os.FileMode(420), modTime: time.Unix(1561038914, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1540715431_add_version.up.sql", size: 265, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc7, 0x4c, 0x36, 0x96, 0xdf, 0x16, 0x10, 0xa6, 0x27, 0x1a, 0x79, 0x8b, 0x42, 0x83, 0x23, 0xc, 0x7e, 0xb6, 0x3d, 0x2, 0xda, 0xa4, 0xb4, 0xd, 0x27, 0x55, 0xba, 0xdc, 0xb2, 0x88, 0x8f, 0xa6}} return a, nil } @@ -220,8 +221,8 @@ func _1541164797_add_installationsDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1541164797_add_installations.down.sql", size: 26, mode: os.FileMode(420), modTime: time.Unix(1561038914, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1541164797_add_installations.down.sql", size: 26, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0xfd, 0xe6, 0xd8, 0xca, 0x3b, 0x38, 0x18, 0xee, 0x0, 0x5f, 0x36, 0x9e, 0x1e, 0xd, 0x19, 0x3e, 0xb4, 0x73, 0x53, 0xe9, 0xa5, 0xac, 0xdd, 0xa1, 0x2f, 0xc7, 0x6c, 0xa8, 0xd9, 0xa, 0x88}} return a, nil } @@ -240,8 +241,8 @@ func _1541164797_add_installationsUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1541164797_add_installations.up.sql", size: 216, mode: os.FileMode(420), modTime: time.Unix(1561038914, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1541164797_add_installations.up.sql", size: 216, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2d, 0x18, 0x26, 0xb8, 0x88, 0x47, 0xdb, 0x83, 0xcc, 0xb6, 0x9d, 0x1c, 0x1, 0xae, 0x2f, 0xde, 0x97, 0x82, 0x3, 0x30, 0xa8, 0x63, 0xa1, 0x78, 0x4b, 0xa5, 0x9, 0x8, 0x75, 0xa2, 0x57, 0x81}} return a, nil } @@ -260,8 +261,8 @@ func _1558084410_add_secretDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1558084410_add_secret.down.sql", size: 56, mode: os.FileMode(420), modTime: time.Unix(1561038914, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1558084410_add_secret.down.sql", size: 56, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x49, 0xb, 0x65, 0xdf, 0x59, 0xbf, 0xe9, 0x5, 0x5b, 0x6f, 0xd5, 0x3a, 0xb7, 0x57, 0xe8, 0x78, 0x38, 0x73, 0x53, 0x57, 0xf7, 0x24, 0x4, 0xe4, 0xa2, 0x49, 0x22, 0xa2, 0xc6, 0xfd, 0x80, 0xa4}} return a, nil } @@ -280,8 +281,8 @@ func _1558084410_add_secretUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1558084410_add_secret.up.sql", size: 301, mode: os.FileMode(420), modTime: time.Unix(1561038914, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1558084410_add_secret.up.sql", size: 301, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0x32, 0x36, 0x8e, 0x47, 0xb0, 0x8f, 0xc1, 0xc6, 0xf7, 0xc6, 0x9f, 0x2d, 0x44, 0x75, 0x2b, 0x26, 0xec, 0x6, 0xa0, 0x7b, 0xa5, 0xbd, 0xc8, 0x76, 0x8a, 0x82, 0x68, 0x2, 0x42, 0xb5, 0xf4}} return a, nil } @@ -300,8 +301,8 @@ func _1558588866_add_versionUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1558588866_add_version.up.sql", size: 57, mode: os.FileMode(420), modTime: time.Unix(1561038914, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1558588866_add_version.up.sql", size: 57, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2a, 0xea, 0x64, 0x39, 0x61, 0x20, 0x83, 0x83, 0xb, 0x2e, 0x79, 0x64, 0xb, 0x53, 0xfa, 0xfe, 0xc6, 0xf7, 0x67, 0x42, 0xd3, 0x4f, 0xdc, 0x7e, 0x30, 0x32, 0xe8, 0x14, 0x41, 0xe9, 0xe7, 0x3b}} return a, nil } @@ -320,8 +321,8 @@ func _1559627659_add_contact_codeDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1559627659_add_contact_code.down.sql", size: 32, mode: os.FileMode(420), modTime: time.Unix(1561038914, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1559627659_add_contact_code.down.sql", size: 32, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x5d, 0x64, 0x6d, 0xce, 0x24, 0x42, 0x20, 0x8d, 0x4f, 0x37, 0xaa, 0x9d, 0xc, 0x57, 0x98, 0xc1, 0xd1, 0x1a, 0x34, 0xcd, 0x9f, 0x8f, 0x34, 0x86, 0xb3, 0xd3, 0xdc, 0xf1, 0x7d, 0xe5, 0x1b, 0x6e}} return a, nil } @@ -340,8 +341,8 @@ func _1559627659_add_contact_codeUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1559627659_add_contact_code.up.sql", size: 198, mode: os.FileMode(420), modTime: time.Unix(1561038914, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1559627659_add_contact_code.up.sql", size: 198, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x16, 0xf6, 0xc2, 0x62, 0x9c, 0xd2, 0xc9, 0x1e, 0xd8, 0xea, 0xaa, 0xea, 0x95, 0x8f, 0x89, 0x6a, 0x85, 0x5d, 0x9d, 0x99, 0x78, 0x3c, 0x90, 0x66, 0x99, 0x3e, 0x4b, 0x19, 0x62, 0xfb, 0x31, 0x4d}} return a, nil } @@ -360,8 +361,8 @@ func _1561059285_add_whisper_keysDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1561059285_add_whisper_keys.down.sql", size: 25, mode: os.FileMode(420), modTime: time.Unix(1561361219, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1561059285_add_whisper_keys.down.sql", size: 25, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb9, 0x31, 0x3f, 0xce, 0xfa, 0x44, 0x36, 0x1b, 0xb0, 0xec, 0x5d, 0xb, 0x90, 0xb, 0x21, 0x4f, 0xd5, 0xe5, 0x50, 0xed, 0xc7, 0x43, 0xdf, 0x83, 0xb4, 0x3a, 0xc1, 0x55, 0x2e, 0x53, 0x7c, 0x67}} return a, nil } @@ -380,8 +381,8 @@ func _1561059285_add_whisper_keysUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1561059285_add_whisper_keys.up.sql", size: 112, mode: os.FileMode(420), modTime: time.Unix(1561361219, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1561059285_add_whisper_keys.up.sql", size: 112, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x25, 0x41, 0xc, 0x92, 0xdd, 0x9e, 0xff, 0x5d, 0xd0, 0x93, 0xe4, 0x24, 0x50, 0x29, 0xcf, 0xc6, 0xf7, 0x49, 0x3c, 0x73, 0xd9, 0x8c, 0xfa, 0xf2, 0xcf, 0xf6, 0x6f, 0xbc, 0x31, 0xe6, 0xf7, 0xe2}} return a, nil } @@ -400,8 +401,8 @@ func _1561368210_add_installation_metadataDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1561368210_add_installation_metadata.down.sql", size: 35, mode: os.FileMode(420), modTime: time.Unix(1561969196, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1561368210_add_installation_metadata.down.sql", size: 35, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa8, 0xde, 0x3f, 0xd2, 0x4a, 0x50, 0x98, 0x56, 0xe3, 0xc0, 0xcd, 0x9d, 0xb0, 0x34, 0x3b, 0xe5, 0x62, 0x18, 0xb5, 0x20, 0xc9, 0x3e, 0xdc, 0x6a, 0x40, 0x36, 0x66, 0xea, 0x51, 0x8c, 0x71, 0xf5}} return a, nil } @@ -420,28 +421,28 @@ func _1561368210_add_installation_metadataUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1561368210_add_installation_metadata.up.sql", size: 267, mode: os.FileMode(420), modTime: time.Unix(1561969196, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "1561368210_add_installation_metadata.up.sql", size: 267, mode: os.FileMode(0644), modTime: time.Unix(1562427968, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb4, 0x71, 0x8f, 0x29, 0xb1, 0xaa, 0xd6, 0xd1, 0x8c, 0x17, 0xef, 0x6c, 0xd5, 0x80, 0xb8, 0x2c, 0xc3, 0xfe, 0xec, 0x24, 0x4d, 0xc8, 0x25, 0xd3, 0xb4, 0xcd, 0xa9, 0xac, 0x63, 0x61, 0xb2, 0x9c}} return a, nil } -var _staticGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x8c\x41\x6a\xc3\x40\x0c\x45\xf7\x73\x8a\xbf\x6c\xa1\x1e\xed\x7b\x82\x52\x12\x08\x24\x17\xd0\xd8\x42\x16\xc6\x23\x33\x52\xee\x9f\x4d\x42\xc8\xf2\xc1\x7b\x8f\x08\x17\x9e\x37\x56\x41\x24\xa7\xcd\x90\xbd\xc9\x12\x2f\xfa\xfa\xbf\xfe\xe0\xef\x76\x3e\x7d\x63\x48\xf8\x7d\xcc\x12\x18\xa6\x6b\xc2\x7a\x3a\x72\x15\x34\xeb\x3c\x4c\xa2\x1c\x1f\xa7\x52\x88\xd4\x7f\x55\xba\x0c\x4e\x81\xfa\xd4\xac\x2f\x9c\x8c\xe9\xd8\x14\xbb\xe9\xe0\x34\xef\x81\xc9\x51\x2b\xd5\x4a\xbb\x44\xb0\x5a\x57\x5a\x1a\xbd\x05\x7a\x86\x55\x1d\xb5\x3c\x02\x00\x00\xff\xff\x97\x9b\xee\x8f\xb4\x00\x00\x00") +var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\x3b\x72\xc3\x30\x0c\x44\x7b\x9d\x62\xc7\x8d\x9b\x88\x6c\x52\xa5\x4b\x99\x3e\x17\x80\x49\x88\xc4\x98\x1f\x85\x80\xfc\xb9\x7d\x46\x4e\x66\xe2\x2e\xed\x0e\xde\xe2\xad\xf7\xf8\xcc\xa2\x58\xa4\x30\x44\xd1\x38\xb0\x2a\x8d\x3b\x4e\x1c\x68\x53\xc6\x21\x89\xe5\xed\xe4\x42\xaf\x5e\x8d\x6c\xd3\x59\xaa\xaf\x92\x06\x19\xfb\xcb\xeb\x61\xf2\x1e\x81\xda\xd1\x90\xa9\xc5\xc2\x8f\x2e\x85\x1a\x0d\x93\x96\x70\x15\xcb\x20\xac\x83\x17\xb9\x39\xbc\x1b\x0a\x93\x1a\x2c\x93\x1d\x15\x96\x19\x81\x94\xf7\x9a\xa5\x0f\xa4\x3e\x9f\xa4\x45\x32\x72\x7b\xf4\xb1\x3c\x25\xbb\x61\xa0\x52\x38\x62\x19\xbd\x3e\x58\xa5\xca\x88\x32\x38\x58\x1f\xf7\x17\x90\x2a\x1b\x1a\x55\xd6\x9d\xcf\x74\x61\xb4\xfe\xfb\x1e\xd4\xe2\xff\x8b\x70\xed\xe3\xac\x20\x05\xdf\x56\x0e\xc6\xd1\x4d\xd3\x4a\xe1\x4c\x89\xf1\x73\x27\xbd\xe9\x34\x79\x9f\xfa\x5b\xe2\xc6\x3b\xf9\xec\x39\xaf\xe7\x04\xfd\x2a\x62\x8c\xb9\xc3\x39\xff\x87\xb9\xd4\xe1\xa6\xef\x00\x00\x00\xff\xff\xcd\x86\x58\x5c\x79\x01\x00\x00") -func staticGoBytes() ([]byte, error) { +func docGoBytes() ([]byte, error) { return bindataRead( - _staticGo, - "static.go", + _docGo, + "doc.go", ) } -func staticGo() (*asset, error) { - bytes, err := staticGoBytes() +func docGo() (*asset, error) { + bytes, err := docGoBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "static.go", size: 180, mode: os.FileMode(420), modTime: time.Unix(1562216541, 0)} - a := &asset{bytes: bytes, info: info} + info := bindataFileInfo{name: "doc.go", size: 377, mode: os.FileMode(0644), modTime: time.Unix(1562955235, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x94, 0xd1, 0x5c, 0x73, 0x47, 0x65, 0xf9, 0x6e, 0xa0, 0xee, 0xb, 0x3d, 0xbe, 0xff, 0xef, 0xae, 0xc9, 0x46, 0x21, 0x85, 0x12, 0x46, 0xa1, 0x73, 0x74, 0xca, 0x71, 0xb1, 0xe1, 0x69, 0xe1, 0x82}} return a, nil } @@ -449,8 +450,8 @@ func staticGo() (*asset, error) { // It returns an error if the asset could not be found or // could not be loaded. func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) @@ -460,6 +461,12 @@ func Asset(name string) ([]byte, error) { return nil, fmt.Errorf("Asset %s not found", name) } +// AssetString returns the asset contents as a string (instead of a []byte). +func AssetString(name string) (string, error) { + data, err := Asset(name) + return string(data), err +} + // MustAsset is like Asset but panics when Asset would return an error. // It simplifies safe initialization of global variables. func MustAsset(name string) []byte { @@ -471,12 +478,18 @@ func MustAsset(name string) []byte { return a } +// MustAssetString is like AssetString but panics when Asset would return an +// error. It simplifies safe initialization of global variables. +func MustAssetString(name string) string { + return string(MustAsset(name)) +} + // AssetInfo loads and returns the asset info for the given name. // It returns an error if the asset could not be found or // could not be loaded. func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { a, err := f() if err != nil { return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) @@ -486,6 +499,33 @@ func AssetInfo(name string) (os.FileInfo, error) { return nil, fmt.Errorf("AssetInfo %s not found", name) } +// AssetDigest returns the digest of the file with the given name. It returns an +// error if the asset could not be found or the digest could not be loaded. +func AssetDigest(name string) ([sha256.Size]byte, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err) + } + return a.digest, nil + } + return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name) +} + +// Digests returns a map of all known files and their checksums. +func Digests() (map[string][sha256.Size]byte, error) { + mp := make(map[string][sha256.Size]byte, len(_bindata)) + for name := range _bindata { + a, err := _bindata[name]() + if err != nil { + return nil, err + } + mp[name] = a.digest + } + return mp, nil +} + // AssetNames returns the names of the assets. func AssetNames() []string { names := make([]string, 0, len(_bindata)) @@ -498,23 +538,40 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ "1536754952_initial_schema.down.sql": _1536754952_initial_schemaDownSql, + "1536754952_initial_schema.up.sql": _1536754952_initial_schemaUpSql, + "1539249977_update_ratchet_info.down.sql": _1539249977_update_ratchet_infoDownSql, + "1539249977_update_ratchet_info.up.sql": _1539249977_update_ratchet_infoUpSql, + "1540715431_add_version.down.sql": _1540715431_add_versionDownSql, + "1540715431_add_version.up.sql": _1540715431_add_versionUpSql, + "1541164797_add_installations.down.sql": _1541164797_add_installationsDownSql, + "1541164797_add_installations.up.sql": _1541164797_add_installationsUpSql, + "1558084410_add_secret.down.sql": _1558084410_add_secretDownSql, + "1558084410_add_secret.up.sql": _1558084410_add_secretUpSql, + "1558588866_add_version.up.sql": _1558588866_add_versionUpSql, + "1559627659_add_contact_code.down.sql": _1559627659_add_contact_codeDownSql, + "1559627659_add_contact_code.up.sql": _1559627659_add_contact_codeUpSql, + "1561059285_add_whisper_keys.down.sql": _1561059285_add_whisper_keysDownSql, + "1561059285_add_whisper_keys.up.sql": _1561059285_add_whisper_keysUpSql, + "1561368210_add_installation_metadata.down.sql": _1561368210_add_installation_metadataDownSql, + "1561368210_add_installation_metadata.up.sql": _1561368210_add_installation_metadataUpSql, - "static.go": staticGo, + + "doc.go": docGo, } // AssetDir returns the file names below a certain @@ -526,15 +583,15 @@ var _bindata = map[string]func() (*asset, error){ // img/ // a.png // b.png -// then AssetDir("data") would return []string{"foo.txt", "img"} -// AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// then AssetDir("data") would return []string{"foo.txt", "img"}, +// AssetDir("data/img") would return []string{"a.png", "b.png"}, +// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and // AssetDir("") will return []string{"data"}. func AssetDir(name string) ([]string, error) { node := _bintree if len(name) != 0 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") + canonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(canonicalName, "/") for _, p := range pathList { node = node.Children[p] if node == nil { @@ -556,28 +613,29 @@ type bintree struct { Func func() (*asset, error) Children map[string]*bintree } + var _bintree = &bintree{nil, map[string]*bintree{ - "1536754952_initial_schema.down.sql": &bintree{_1536754952_initial_schemaDownSql, map[string]*bintree{}}, - "1536754952_initial_schema.up.sql": &bintree{_1536754952_initial_schemaUpSql, map[string]*bintree{}}, - "1539249977_update_ratchet_info.down.sql": &bintree{_1539249977_update_ratchet_infoDownSql, map[string]*bintree{}}, - "1539249977_update_ratchet_info.up.sql": &bintree{_1539249977_update_ratchet_infoUpSql, map[string]*bintree{}}, - "1540715431_add_version.down.sql": &bintree{_1540715431_add_versionDownSql, map[string]*bintree{}}, - "1540715431_add_version.up.sql": &bintree{_1540715431_add_versionUpSql, map[string]*bintree{}}, - "1541164797_add_installations.down.sql": &bintree{_1541164797_add_installationsDownSql, map[string]*bintree{}}, - "1541164797_add_installations.up.sql": &bintree{_1541164797_add_installationsUpSql, map[string]*bintree{}}, - "1558084410_add_secret.down.sql": &bintree{_1558084410_add_secretDownSql, map[string]*bintree{}}, - "1558084410_add_secret.up.sql": &bintree{_1558084410_add_secretUpSql, map[string]*bintree{}}, - "1558588866_add_version.up.sql": &bintree{_1558588866_add_versionUpSql, map[string]*bintree{}}, - "1559627659_add_contact_code.down.sql": &bintree{_1559627659_add_contact_codeDownSql, map[string]*bintree{}}, - "1559627659_add_contact_code.up.sql": &bintree{_1559627659_add_contact_codeUpSql, map[string]*bintree{}}, - "1561059285_add_whisper_keys.down.sql": &bintree{_1561059285_add_whisper_keysDownSql, map[string]*bintree{}}, - "1561059285_add_whisper_keys.up.sql": &bintree{_1561059285_add_whisper_keysUpSql, map[string]*bintree{}}, + "1536754952_initial_schema.down.sql": &bintree{_1536754952_initial_schemaDownSql, map[string]*bintree{}}, + "1536754952_initial_schema.up.sql": &bintree{_1536754952_initial_schemaUpSql, map[string]*bintree{}}, + "1539249977_update_ratchet_info.down.sql": &bintree{_1539249977_update_ratchet_infoDownSql, map[string]*bintree{}}, + "1539249977_update_ratchet_info.up.sql": &bintree{_1539249977_update_ratchet_infoUpSql, map[string]*bintree{}}, + "1540715431_add_version.down.sql": &bintree{_1540715431_add_versionDownSql, map[string]*bintree{}}, + "1540715431_add_version.up.sql": &bintree{_1540715431_add_versionUpSql, map[string]*bintree{}}, + "1541164797_add_installations.down.sql": &bintree{_1541164797_add_installationsDownSql, map[string]*bintree{}}, + "1541164797_add_installations.up.sql": &bintree{_1541164797_add_installationsUpSql, map[string]*bintree{}}, + "1558084410_add_secret.down.sql": &bintree{_1558084410_add_secretDownSql, map[string]*bintree{}}, + "1558084410_add_secret.up.sql": &bintree{_1558084410_add_secretUpSql, map[string]*bintree{}}, + "1558588866_add_version.up.sql": &bintree{_1558588866_add_versionUpSql, map[string]*bintree{}}, + "1559627659_add_contact_code.down.sql": &bintree{_1559627659_add_contact_codeDownSql, map[string]*bintree{}}, + "1559627659_add_contact_code.up.sql": &bintree{_1559627659_add_contact_codeUpSql, map[string]*bintree{}}, + "1561059285_add_whisper_keys.down.sql": &bintree{_1561059285_add_whisper_keysDownSql, map[string]*bintree{}}, + "1561059285_add_whisper_keys.up.sql": &bintree{_1561059285_add_whisper_keysUpSql, map[string]*bintree{}}, "1561368210_add_installation_metadata.down.sql": &bintree{_1561368210_add_installation_metadataDownSql, map[string]*bintree{}}, - "1561368210_add_installation_metadata.up.sql": &bintree{_1561368210_add_installation_metadataUpSql, map[string]*bintree{}}, - "static.go": &bintree{staticGo, map[string]*bintree{}}, + "1561368210_add_installation_metadata.up.sql": &bintree{_1561368210_add_installation_metadataUpSql, map[string]*bintree{}}, + "doc.go": &bintree{docGo, map[string]*bintree{}}, }} -// RestoreAsset restores an asset under the given directory +// RestoreAsset restores an asset under the given directory. func RestoreAsset(dir, name string) error { data, err := Asset(name) if err != nil { @@ -595,14 +653,10 @@ func RestoreAsset(dir, name string) error { if err != nil { return err } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil + return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) } -// RestoreAssets restores an asset under the given directory recursively +// RestoreAssets restores an asset under the given directory recursively. func RestoreAssets(dir, name string) error { children, err := AssetDir(name) // File @@ -620,7 +674,6 @@ func RestoreAssets(dir, name string) error { } func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) + canonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) } - diff --git a/messaging/multidevice/service.go b/vendor/github.com/status-im/status-protocol-go/encryption/multidevice/multidevice.go similarity index 71% rename from messaging/multidevice/service.go rename to vendor/github.com/status-im/status-protocol-go/encryption/multidevice/multidevice.go index 05d572d00..e91c908f3 100644 --- a/messaging/multidevice/service.go +++ b/vendor/github.com/status-im/status-protocol-go/encryption/multidevice/multidevice.go @@ -2,6 +2,7 @@ package multidevice import ( "crypto/ecdsa" + "database/sql" "github.com/ethereum/go-ethereum/crypto" ) @@ -36,28 +37,28 @@ type Config struct { InstallationID string } -func New(config *Config, persistence Persistence) *Service { - return &Service{ - config: config, - persistence: persistence, - } -} - -type Service struct { - persistence Persistence +type Multidevice struct { + persistence *sqlitePersistence config *Config } -func (s *Service) InstallationID() string { +func New(db *sql.DB, config *Config) *Multidevice { + return &Multidevice{ + config: config, + persistence: newSQLitePersistence(db), + } +} + +func (s *Multidevice) InstallationID() string { return s.config.InstallationID } -func (s *Service) GetActiveInstallations(identity *ecdsa.PublicKey) ([]*Installation, error) { +func (s *Multidevice) GetActiveInstallations(identity *ecdsa.PublicKey) ([]*Installation, error) { identityC := crypto.CompressPubkey(identity) return s.persistence.GetActiveInstallations(s.config.MaxInstallations, identityC) } -func (s *Service) GetOurActiveInstallations(identity *ecdsa.PublicKey) ([]*Installation, error) { +func (s *Multidevice) GetOurActiveInstallations(identity *ecdsa.PublicKey) ([]*Installation, error) { identityC := crypto.CompressPubkey(identity) installations, err := s.persistence.GetActiveInstallations(s.config.MaxInstallations-1, identityC) if err != nil { @@ -72,7 +73,7 @@ func (s *Service) GetOurActiveInstallations(identity *ecdsa.PublicKey) ([]*Insta return installations, nil } -func (s *Service) GetOurInstallations(identity *ecdsa.PublicKey) ([]*Installation, error) { +func (s *Multidevice) GetOurInstallations(identity *ecdsa.PublicKey) ([]*Installation, error) { var found bool identityC := crypto.CompressPubkey(identity) installations, err := s.persistence.GetInstallations(identityC) @@ -99,21 +100,21 @@ func (s *Service) GetOurInstallations(identity *ecdsa.PublicKey) ([]*Installatio return installations, nil } -func (s *Service) AddInstallations(identity []byte, timestamp int64, installations []*Installation, defaultEnabled bool) ([]*Installation, error) { +func (s *Multidevice) AddInstallations(identity []byte, timestamp int64, installations []*Installation, defaultEnabled bool) ([]*Installation, error) { return s.persistence.AddInstallations(identity, timestamp, installations, defaultEnabled) } -func (s *Service) SetInstallationMetadata(identity *ecdsa.PublicKey, installationID string, metadata *InstallationMetadata) error { +func (s *Multidevice) SetInstallationMetadata(identity *ecdsa.PublicKey, installationID string, metadata *InstallationMetadata) error { identityC := crypto.CompressPubkey(identity) return s.persistence.SetInstallationMetadata(identityC, installationID, metadata) } -func (s *Service) EnableInstallation(identity *ecdsa.PublicKey, installationID string) error { +func (s *Multidevice) EnableInstallation(identity *ecdsa.PublicKey, installationID string) error { identityC := crypto.CompressPubkey(identity) return s.persistence.EnableInstallation(identityC, installationID) } -func (s *Service) DisableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error { +func (s *Multidevice) DisableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error { myIdentityKeyC := crypto.CompressPubkey(myIdentityKey) return s.persistence.DisableInstallation(myIdentityKeyC, installationID) } diff --git a/messaging/multidevice/sql_lite_persistence.go b/vendor/github.com/status-im/status-protocol-go/encryption/multidevice/persistence.go similarity index 85% rename from messaging/multidevice/sql_lite_persistence.go rename to vendor/github.com/status-im/status-protocol-go/encryption/multidevice/persistence.go index 501cf8a3a..6acf93837 100644 --- a/messaging/multidevice/sql_lite_persistence.go +++ b/vendor/github.com/status-im/status-protocol-go/encryption/multidevice/persistence.go @@ -1,21 +1,17 @@ package multidevice -import ( - "database/sql" -) +import "database/sql" -// SQLLitePersistence represents a persistence service tied to an SQLite database -type SQLLitePersistence struct { +type sqlitePersistence struct { db *sql.DB } -// NewSQLLitePersistence creates a new SQLLitePersistence instance, given a path and a key -func NewSQLLitePersistence(db *sql.DB) *SQLLitePersistence { - return &SQLLitePersistence{db: db} +func newSQLitePersistence(db *sql.DB) *sqlitePersistence { + return &sqlitePersistence{db: db} } // GetActiveInstallations returns the active installations for a given identity -func (s *SQLLitePersistence) GetActiveInstallations(maxInstallations int, identity []byte) ([]*Installation, error) { +func (s *sqlitePersistence) GetActiveInstallations(maxInstallations int, identity []byte) ([]*Installation, error) { stmt, err := s.db.Prepare(`SELECT installation_id, version FROM installations WHERE enabled = 1 AND identity = ? @@ -59,7 +55,7 @@ func (s *SQLLitePersistence) GetActiveInstallations(maxInstallations int, identi // we both return the installations & the metadata // metadata is currently stored in a separate table, as in some cases we // might have metadata for a device, but no other information on the device -func (s *SQLLitePersistence) GetInstallations(identity []byte) ([]*Installation, error) { +func (s *sqlitePersistence) GetInstallations(identity []byte) ([]*Installation, error) { installationMap := make(map[string]*Installation) var installations []*Installation @@ -141,7 +137,7 @@ func (s *SQLLitePersistence) GetInstallations(identity []byte) ([]*Installation, } // AddInstallations adds the installations for a given identity, maintaining the enabled flag -func (s *SQLLitePersistence) AddInstallations(identity []byte, timestamp int64, installations []*Installation, defaultEnabled bool) ([]*Installation, error) { +func (s *sqlitePersistence) AddInstallations(identity []byte, timestamp int64, installations []*Installation, defaultEnabled bool) ([]*Installation, error) { tx, err := s.db.Begin() if err != nil { return nil, err @@ -230,7 +226,7 @@ func (s *SQLLitePersistence) AddInstallations(identity []byte, timestamp int64, } // EnableInstallation enables the installation -func (s *SQLLitePersistence) EnableInstallation(identity []byte, installationID string) error { +func (s *sqlitePersistence) EnableInstallation(identity []byte, installationID string) error { stmt, err := s.db.Prepare(`UPDATE installations SET enabled = 1 WHERE identity = ? AND installation_id = ?`) @@ -244,8 +240,7 @@ func (s *SQLLitePersistence) EnableInstallation(identity []byte, installationID } // DisableInstallation disable the installation -func (s *SQLLitePersistence) DisableInstallation(identity []byte, installationID string) error { - +func (s *sqlitePersistence) DisableInstallation(identity []byte, installationID string) error { stmt, err := s.db.Prepare(`UPDATE installations SET enabled = 0 WHERE identity = ? AND installation_id = ?`) @@ -258,7 +253,7 @@ func (s *SQLLitePersistence) DisableInstallation(identity []byte, installationID } // SetInstallationMetadata sets the metadata for a given installation -func (s *SQLLitePersistence) SetInstallationMetadata(identity []byte, installationID string, metadata *InstallationMetadata) error { +func (s *sqlitePersistence) SetInstallationMetadata(identity []byte, installationID string, metadata *InstallationMetadata) error { stmt, err := s.db.Prepare(`INSERT INTO installation_metadata(name, device_type, fcm_token, identity, installation_id) VALUES(?,?,?,?,?)`) if err != nil { return err diff --git a/messaging/chat/sql_lite_persistence.go b/vendor/github.com/status-im/status-protocol-go/encryption/persistence.go similarity index 75% rename from messaging/chat/sql_lite_persistence.go rename to vendor/github.com/status-im/status-protocol-go/encryption/persistence.go index 17f33148a..8b3b55dc0 100644 --- a/messaging/chat/sql_lite_persistence.go +++ b/vendor/github.com/status-im/status-protocol-go/encryption/persistence.go @@ -1,4 +1,4 @@ -package chat +package encryption import ( "crypto/ecdsa" @@ -8,102 +8,51 @@ import ( "github.com/ethereum/go-ethereum/crypto" dr "github.com/status-im/doubleratchet" - "github.com/status-im/status-go/messaging/chat/protobuf" - ecrypto "github.com/status-im/status-go/messaging/crypto" - msgdb "github.com/status-im/status-go/messaging/db" - "github.com/status-im/status-go/messaging/multidevice" - "github.com/status-im/status-go/messaging/sharedsecret" + ecrypto "github.com/status-im/status-protocol-go/crypto" + "github.com/status-im/status-protocol-go/encryption/multidevice" ) -// A safe max number of rows +// RatchetInfo holds the current ratchet state. +type RatchetInfo struct { + ID []byte + Sk []byte + PrivateKey []byte + PublicKey []byte + Identity []byte + BundleID []byte + EphemeralKey []byte + InstallationID string +} + +// A safe max number of rows. const maxNumberOfRows = 100000000 -// SQLLitePersistence represents a persistence service tied to an SQLite database -type SQLLitePersistence struct { - DB *sql.DB - keysStorage dr.KeysStorage - sessionStorage dr.SessionStorage - secretStorage sharedsecret.Persistence - multideviceStorage multidevice.Persistence +type sqlitePersistence struct { + DB *sql.DB + keysStorage dr.KeysStorage + sessionStorage dr.SessionStorage } -// SQLLiteKeysStorage represents a keys persistence service tied to an SQLite database -type SQLLiteKeysStorage struct { - db *sql.DB -} - -// SQLLiteSessionStorage represents a session persistence service tied to an SQLite database -type SQLLiteSessionStorage struct { - db *sql.DB -} - -// NewSQLLitePersistence creates a new SQLLitePersistence instance, given a path and a key -func NewSQLLitePersistence(path string, key string) (*SQLLitePersistence, error) { - s := &SQLLitePersistence{} - - if err := s.Open(path, key); err != nil { - return nil, err - } - - s.keysStorage = NewSQLLiteKeysStorage(s.DB) - - s.sessionStorage = NewSQLLiteSessionStorage(s.DB) - - s.secretStorage = sharedsecret.NewSQLLitePersistence(s.DB) - - s.multideviceStorage = multidevice.NewSQLLitePersistence(s.DB) - - return s, nil -} - -// NewSQLLiteKeysStorage creates a new SQLLiteKeysStorage instance associated with the specified database -func NewSQLLiteKeysStorage(db *sql.DB) *SQLLiteKeysStorage { - return &SQLLiteKeysStorage{ - db: db, - } -} - -// NewSQLLiteSessionStorage creates a new SQLLiteSessionStorage instance associated with the specified database -func NewSQLLiteSessionStorage(db *sql.DB) *SQLLiteSessionStorage { - return &SQLLiteSessionStorage{ - db: db, +func newSQLitePersistence(db *sql.DB) *sqlitePersistence { + return &sqlitePersistence{ + DB: db, + keysStorage: newSQLiteKeysStorage(db), + sessionStorage: newSQLiteSessionStorage(db), } } // GetKeysStorage returns the associated double ratchet KeysStorage object -func (s *SQLLitePersistence) GetKeysStorage() dr.KeysStorage { +func (s *sqlitePersistence) KeysStorage() dr.KeysStorage { return s.keysStorage } // GetSessionStorage returns the associated double ratchet SessionStorage object -func (s *SQLLitePersistence) GetSessionStorage() dr.SessionStorage { +func (s *sqlitePersistence) SessionStorage() dr.SessionStorage { return s.sessionStorage } -// GetSharedSecretStorage returns the associated secretStorageObject -func (s *SQLLitePersistence) GetSharedSecretStorage() sharedsecret.Persistence { - return s.secretStorage -} - -// GetMultideviceStorage returns the associated multideviceStorage -func (s *SQLLitePersistence) GetMultideviceStorage() multidevice.Persistence { - return s.multideviceStorage -} - -// Open opens a file at the specified path -func (s *SQLLitePersistence) Open(path string, key string) error { - db, err := msgdb.Open(path, key, msgdb.KdfIterationsNumber) - if err != nil { - return err - } - - s.DB = db - - return nil -} - // AddPrivateBundle adds the specified BundleContainer to the database -func (s *SQLLitePersistence) AddPrivateBundle(bc *protobuf.BundleContainer) error { +func (s *sqlitePersistence) AddPrivateBundle(bc *BundleContainer) error { tx, err := s.DB.Begin() if err != nil { return err @@ -157,7 +106,7 @@ func (s *SQLLitePersistence) AddPrivateBundle(bc *protobuf.BundleContainer) erro } // AddPublicBundle adds the specified Bundle to the database -func (s *SQLLitePersistence) AddPublicBundle(b *protobuf.Bundle) error { +func (s *sqlitePersistence) AddPublicBundle(b *Bundle) error { tx, err := s.DB.Begin() if err != nil { @@ -210,7 +159,7 @@ func (s *SQLLitePersistence) AddPublicBundle(b *protobuf.Bundle) error { } // GetAnyPrivateBundle retrieves any bundle from the database containing a private key -func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte, installations []*multidevice.Installation) (*protobuf.BundleContainer, error) { +func (s *sqlitePersistence) GetAnyPrivateBundle(myIdentityKey []byte, installations []*multidevice.Installation) (*BundleContainer, error) { versions := make(map[string]uint32) /* #nosec */ @@ -246,11 +195,11 @@ func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte, installat defer rows.Close() - bundle := &protobuf.Bundle{ - SignedPreKeys: make(map[string]*protobuf.SignedPreKey), + bundle := &Bundle{ + SignedPreKeys: make(map[string]*SignedPreKey), } - bundleContainer := &protobuf.BundleContainer{ + bundleContainer := &BundleContainer{ Bundle: bundle, } @@ -274,7 +223,7 @@ func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte, installat bundle.Timestamp = timestamp } - bundle.SignedPreKeys[installationID] = &protobuf.SignedPreKey{ + bundle.SignedPreKeys[installationID] = &SignedPreKey{ SignedPreKey: signedPreKey, Version: version, ProtocolVersion: versions[installationID], @@ -292,7 +241,7 @@ func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte, installat } // GetPrivateKeyBundle retrieves a private key for a bundle from the database -func (s *SQLLitePersistence) GetPrivateKeyBundle(bundleID []byte) ([]byte, error) { +func (s *sqlitePersistence) GetPrivateKeyBundle(bundleID []byte) ([]byte, error) { stmt, err := s.DB.Prepare(`SELECT private_key FROM bundles WHERE signed_pre_key = ? LIMIT 1`) @@ -315,7 +264,7 @@ func (s *SQLLitePersistence) GetPrivateKeyBundle(bundleID []byte) ([]byte, error } // MarkBundleExpired expires any private bundle for a given identity -func (s *SQLLitePersistence) MarkBundleExpired(identity []byte) error { +func (s *sqlitePersistence) MarkBundleExpired(identity []byte) error { stmt, err := s.DB.Prepare(`UPDATE bundles SET expired = 1 WHERE identity = ? AND private_key IS NOT NULL`) @@ -330,7 +279,7 @@ func (s *SQLLitePersistence) MarkBundleExpired(identity []byte) error { } // GetPublicBundle retrieves an existing Bundle for the specified public key from the database -func (s *SQLLitePersistence) GetPublicBundle(publicKey *ecdsa.PublicKey, installations []*multidevice.Installation) (*protobuf.Bundle, error) { +func (s *sqlitePersistence) GetPublicBundle(publicKey *ecdsa.PublicKey, installations []*multidevice.Installation) (*Bundle, error) { if len(installations) == 0 { return nil, nil @@ -367,9 +316,9 @@ func (s *SQLLitePersistence) GetPublicBundle(publicKey *ecdsa.PublicKey, install defer rows.Close() - bundle := &protobuf.Bundle{ + bundle := &Bundle{ Identity: identity, - SignedPreKeys: make(map[string]*protobuf.SignedPreKey), + SignedPreKeys: make(map[string]*SignedPreKey), } for rows.Next() { @@ -386,7 +335,7 @@ func (s *SQLLitePersistence) GetPublicBundle(publicKey *ecdsa.PublicKey, install return nil, err } - bundle.SignedPreKeys[installationID] = &protobuf.SignedPreKey{ + bundle.SignedPreKeys[installationID] = &SignedPreKey{ SignedPreKey: signedPreKey, Version: version, ProtocolVersion: versions[installationID], @@ -403,7 +352,7 @@ func (s *SQLLitePersistence) GetPublicBundle(publicKey *ecdsa.PublicKey, install } // AddRatchetInfo persists the specified ratchet info into the database -func (s *SQLLitePersistence) AddRatchetInfo(key []byte, identity []byte, bundleID []byte, ephemeralKey []byte, installationID string) error { +func (s *sqlitePersistence) AddRatchetInfo(key []byte, identity []byte, bundleID []byte, ephemeralKey []byte, installationID string) error { stmt, err := s.DB.Prepare(`INSERT INTO ratchet_info_v2(symmetric_key, identity, bundle_id, ephemeral_key, installation_id) VALUES(?, ?, ?, ?, ?)`) if err != nil { @@ -423,7 +372,7 @@ func (s *SQLLitePersistence) AddRatchetInfo(key []byte, identity []byte, bundleI } // GetRatchetInfo retrieves the existing RatchetInfo for a specified bundle ID and interlocutor public key from the database -func (s *SQLLitePersistence) GetRatchetInfo(bundleID []byte, theirIdentity []byte, installationID string) (*RatchetInfo, error) { +func (s *sqlitePersistence) GetRatchetInfo(bundleID []byte, theirIdentity []byte, installationID string) (*RatchetInfo, error) { stmt, err := s.DB.Prepare(`SELECT ratchet_info_v2.identity, ratchet_info_v2.symmetric_key, bundles.private_key, bundles.signed_pre_key, ratchet_info_v2.ephemeral_key, ratchet_info_v2.installation_id FROM ratchet_info_v2 JOIN bundles ON bundle_id = signed_pre_key WHERE ratchet_info_v2.identity = ? AND ratchet_info_v2.installation_id = ? AND bundle_id = ? @@ -457,7 +406,7 @@ func (s *SQLLitePersistence) GetRatchetInfo(bundleID []byte, theirIdentity []byt } // GetAnyRatchetInfo retrieves any existing RatchetInfo for a specified interlocutor public key from the database -func (s *SQLLitePersistence) GetAnyRatchetInfo(identity []byte, installationID string) (*RatchetInfo, error) { +func (s *sqlitePersistence) GetAnyRatchetInfo(identity []byte, installationID string) (*RatchetInfo, error) { stmt, err := s.DB.Prepare(`SELECT symmetric_key, bundles.private_key, signed_pre_key, bundle_id, ephemeral_key FROM ratchet_info_v2 JOIN bundles ON bundle_id = signed_pre_key WHERE expired = 0 AND ratchet_info_v2.identity = ? AND ratchet_info_v2.installation_id = ? @@ -492,7 +441,7 @@ func (s *SQLLitePersistence) GetAnyRatchetInfo(identity []byte, installationID s // RatchetInfoConfirmed clears the ephemeral key in the RatchetInfo // associated with the specified bundle ID and interlocutor identity public key -func (s *SQLLitePersistence) RatchetInfoConfirmed(bundleID []byte, theirIdentity []byte, installationID string) error { +func (s *sqlitePersistence) RatchetInfoConfirmed(bundleID []byte, theirIdentity []byte, installationID string) error { stmt, err := s.DB.Prepare(`UPDATE ratchet_info_v2 SET ephemeral_key = NULL WHERE identity = ? AND bundle_id = ? AND installation_id = ?`) @@ -510,8 +459,18 @@ func (s *SQLLitePersistence) RatchetInfoConfirmed(bundleID []byte, theirIdentity return err } +type sqliteKeysStorage struct { + db *sql.DB +} + +func newSQLiteKeysStorage(db *sql.DB) *sqliteKeysStorage { + return &sqliteKeysStorage{ + db: db, + } +} + // Get retrieves the message key for a specified public key and message number -func (s *SQLLiteKeysStorage) Get(pubKey dr.Key, msgNum uint) (dr.Key, bool, error) { +func (s *sqliteKeysStorage) Get(pubKey dr.Key, msgNum uint) (dr.Key, bool, error) { var keyBytes []byte var key [32]byte stmt, err := s.db.Prepare(`SELECT message_key @@ -537,7 +496,7 @@ func (s *SQLLiteKeysStorage) Get(pubKey dr.Key, msgNum uint) (dr.Key, bool, erro } // Put stores a key with the specified public key, message number and message key -func (s *SQLLiteKeysStorage) Put(sessionID []byte, pubKey dr.Key, msgNum uint, mk dr.Key, seqNum uint) error { +func (s *sqliteKeysStorage) Put(sessionID []byte, pubKey dr.Key, msgNum uint, mk dr.Key, seqNum uint) error { stmt, err := s.db.Prepare(`INSERT INTO keys(session_id, public_key, msg_num, message_key, seq_num) VALUES(?, ?, ?, ?, ?)`) if err != nil { @@ -557,7 +516,7 @@ func (s *SQLLiteKeysStorage) Put(sessionID []byte, pubKey dr.Key, msgNum uint, m } // DeleteOldMks caps remove any key < seq_num, included -func (s *SQLLiteKeysStorage) DeleteOldMks(sessionID []byte, deleteUntil uint) error { +func (s *sqliteKeysStorage) DeleteOldMks(sessionID []byte, deleteUntil uint) error { stmt, err := s.db.Prepare(`DELETE FROM keys WHERE session_id = ? AND seq_num <= ?`) if err != nil { @@ -574,7 +533,7 @@ func (s *SQLLiteKeysStorage) DeleteOldMks(sessionID []byte, deleteUntil uint) er } // TruncateMks caps the number of keys to maxKeysPerSession deleting them in FIFO fashion -func (s *SQLLiteKeysStorage) TruncateMks(sessionID []byte, maxKeysPerSession int) error { +func (s *sqliteKeysStorage) TruncateMks(sessionID []byte, maxKeysPerSession int) error { stmt, err := s.db.Prepare(`DELETE FROM keys WHERE rowid IN (SELECT rowid FROM keys WHERE session_id = ? ORDER BY seq_num DESC LIMIT ? OFFSET ?)`) if err != nil { @@ -593,7 +552,7 @@ func (s *SQLLiteKeysStorage) TruncateMks(sessionID []byte, maxKeysPerSession int } // DeleteMk deletes the key with the specified public key and message key -func (s *SQLLiteKeysStorage) DeleteMk(pubKey dr.Key, msgNum uint) error { +func (s *sqliteKeysStorage) DeleteMk(pubKey dr.Key, msgNum uint) error { stmt, err := s.db.Prepare(`DELETE FROM keys WHERE public_key = ? AND msg_num = ?`) if err != nil { @@ -610,7 +569,7 @@ func (s *SQLLiteKeysStorage) DeleteMk(pubKey dr.Key, msgNum uint) error { } // Count returns the count of keys with the specified public key -func (s *SQLLiteKeysStorage) Count(pubKey dr.Key) (uint, error) { +func (s *sqliteKeysStorage) Count(pubKey dr.Key) (uint, error) { stmt, err := s.db.Prepare(`SELECT COUNT(1) FROM keys WHERE public_key = ?`) @@ -629,7 +588,7 @@ func (s *SQLLiteKeysStorage) Count(pubKey dr.Key) (uint, error) { } // CountAll returns the count of keys with the specified public key -func (s *SQLLiteKeysStorage) CountAll() (uint, error) { +func (s *sqliteKeysStorage) CountAll() (uint, error) { stmt, err := s.db.Prepare(`SELECT COUNT(1) FROM keys`) if err != nil { @@ -647,12 +606,22 @@ func (s *SQLLiteKeysStorage) CountAll() (uint, error) { } // All returns nil -func (s *SQLLiteKeysStorage) All() (map[dr.Key]map[uint]dr.Key, error) { +func (s *sqliteKeysStorage) All() (map[dr.Key]map[uint]dr.Key, error) { return nil, nil } +type sqliteSessionStorage struct { + db *sql.DB +} + +func newSQLiteSessionStorage(db *sql.DB) *sqliteSessionStorage { + return &sqliteSessionStorage{ + db: db, + } +} + // Save persists the specified double ratchet state -func (s *SQLLiteSessionStorage) Save(id []byte, state *dr.State) error { +func (s *sqliteSessionStorage) Save(id []byte, state *dr.State) error { dhr := state.DHr[:] dhs := state.DHs dhsPublic := dhs.PublicKey() @@ -695,7 +664,7 @@ func (s *SQLLiteSessionStorage) Save(id []byte, state *dr.State) error { } // Load retrieves the double ratchet state for a given ID -func (s *SQLLiteSessionStorage) Load(id []byte) (*dr.State, error) { +func (s *sqliteSessionStorage) Load(id []byte) (*dr.State, error) { stmt, err := s.db.Prepare(`SELECT dhr, dhs_public, dhs_private, root_chain_key, send_chain_key, send_chain_n, recv_chain_key, recv_chain_n, pn, step, keys_count FROM sessions WHERE id = ?`) diff --git a/vendor/github.com/status-im/status-protocol-go/encryption/protocol.go b/vendor/github.com/status-im/status-protocol-go/encryption/protocol.go new file mode 100644 index 000000000..91c6e6d10 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/encryption/protocol.go @@ -0,0 +1,533 @@ +package encryption + +import ( + "bytes" + "crypto/ecdsa" + "fmt" + + "go.uber.org/zap" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" + + "github.com/status-im/status-protocol-go/encryption/multidevice" + "github.com/status-im/status-protocol-go/encryption/publisher" + "github.com/status-im/status-protocol-go/encryption/sharedsecret" +) + +//go:generate protoc --go_out=. ./protocol_message.proto + +const ( + protocolVersion = 1 + sharedSecretNegotiationVersion = 1 + partitionedTopicMinVersion = 1 + defaultMinVersion = 0 +) + +type PartitionTopicMode int + +const ( + PartitionTopicNoSupport PartitionTopicMode = iota + PartitionTopicV1 +) + +type ProtocolMessageSpec struct { + Message *ProtocolMessage + // Installations is the targeted devices + Installations []*multidevice.Installation + // SharedSecret is a shared secret established among the installations + SharedSecret []byte + // Public means that the spec contains a public wrapped message + Public bool +} + +func (p *ProtocolMessageSpec) MinVersion() uint32 { + if len(p.Installations) == 0 { + return defaultMinVersion + } + + version := p.Installations[0].Version + + for _, installation := range p.Installations[1:] { + if installation.Version < version { + version = installation.Version + } + } + return version +} + +func (p *ProtocolMessageSpec) PartitionedTopicMode() PartitionTopicMode { + if p.MinVersion() >= partitionedTopicMinVersion { + return PartitionTopicV1 + } + return PartitionTopicNoSupport +} + +type Protocol struct { + encryptor *encryptor + secret *sharedsecret.SharedSecret + multidevice *multidevice.Multidevice + publisher *publisher.Publisher + + onAddedBundlesHandler func([]*multidevice.Installation) + onNewSharedSecretHandler func([]*sharedsecret.Secret) + onSendContactCodeHandler func(*ProtocolMessageSpec) + + logger *zap.Logger +} + +var ( + // ErrNoPayload means that there was no payload found in the received protocol message. + ErrNoPayload = errors.New("no payload") +) + +// New creates a new ProtocolService instance +func New( + dbPath string, + dbKey string, + installationID string, + addedBundlesHandler func([]*multidevice.Installation), + onNewSharedSecretHandler func([]*sharedsecret.Secret), + onSendContactCodeHandler func(*ProtocolMessageSpec), + logger *zap.Logger, +) (*Protocol, error) { + return NewWithEncryptorConfig( + dbPath, + dbKey, + installationID, + defaultEncryptorConfig(installationID, logger), + addedBundlesHandler, + onNewSharedSecretHandler, + onSendContactCodeHandler, + logger, + ) +} + +func NewWithEncryptorConfig( + dbPath string, + dbKey string, + installationID string, + encryptorConfig encryptorConfig, + addedBundlesHandler func([]*multidevice.Installation), + onNewSharedSecretHandler func([]*sharedsecret.Secret), + onSendContactCodeHandler func(*ProtocolMessageSpec), + logger *zap.Logger, +) (*Protocol, error) { + encryptor, err := newEncryptor(dbPath, dbKey, encryptorConfig) + if err != nil { + return nil, err + } + + // DB and migrations are shared between encryption package + // and its sub-packages. + db := encryptor.persistence.DB + + return &Protocol{ + encryptor: encryptor, + secret: sharedsecret.New(db, logger), + multidevice: multidevice.New(db, &multidevice.Config{ + MaxInstallations: 3, + ProtocolVersion: protocolVersion, + InstallationID: installationID, + }), + publisher: publisher.New(db, logger), + onAddedBundlesHandler: addedBundlesHandler, + onNewSharedSecretHandler: onNewSharedSecretHandler, + onSendContactCodeHandler: onSendContactCodeHandler, + logger: logger.With(zap.Namespace("Protocol")), + }, nil +} + +func (p *Protocol) Start(myIdentity *ecdsa.PrivateKey) error { + // Propagate currently cached shared secrets. + secrets, err := p.secret.All() + if err != nil { + return errors.Wrap(err, "failed to get all secrets") + } + p.onNewSharedSecretHandler(secrets) + + // Handle Publisher system messages. + publisherCh := p.publisher.Start() + + go func() { + for range publisherCh { + messageSpec, err := p.buildContactCodeMessage(myIdentity) + if err != nil { + p.logger.Error("failed to build contact code message", + zap.String("site", "Start"), + zap.Error(err)) + continue + } + + p.onSendContactCodeHandler(messageSpec) + } + }() + + return nil +} + +func (p *Protocol) addBundle(myIdentityKey *ecdsa.PrivateKey, msg *ProtocolMessage, sendSingle bool) error { + logger := p.logger.With(zap.String("site", "addBundle")) + + // Get a bundle + installations, err := p.multidevice.GetOurActiveInstallations(&myIdentityKey.PublicKey) + if err != nil { + return err + } + + logger.Info("adding bundle to the message", + zap.Any("installations", installations), + ) + + bundle, err := p.encryptor.CreateBundle(myIdentityKey, installations) + if err != nil { + return err + } + + if sendSingle { + // DEPRECATED: This is only for backward compatibility, remove once not + // an issue anymore + msg.Bundle = bundle + } else { + msg.Bundles = []*Bundle{bundle} + } + + return nil +} + +// BuildPublicMessage marshals a public chat message given the user identity private key and a payload +func (p *Protocol) BuildPublicMessage(myIdentityKey *ecdsa.PrivateKey, payload []byte) (*ProtocolMessageSpec, error) { + // Build message not encrypted + message := &ProtocolMessage{ + InstallationId: p.encryptor.config.InstallationID, + PublicMessage: payload, + } + + err := p.addBundle(myIdentityKey, message, false) + if err != nil { + return nil, err + } + + return &ProtocolMessageSpec{Message: message, Public: true}, nil +} + +// buildContactCodeMessage creates a contact code message. It's a public message +// without any data but it carries bundle information. +func (p *Protocol) buildContactCodeMessage(myIdentityKey *ecdsa.PrivateKey) (*ProtocolMessageSpec, error) { + return p.BuildPublicMessage(myIdentityKey, nil) +} + +// BuildDirectMessage returns a 1:1 chat message and optionally a negotiated topic given the user identity private key, the recipient's public key, and a payload +func (p *Protocol) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, payload []byte) (*ProtocolMessageSpec, error) { + logger := p.logger.With( + zap.String("site", "BuildDirectMessage"), + zap.Binary("public-key", crypto.FromECDSAPub(publicKey)), + ) + + logger.Debug("building direct message") + + // Get recipients installations. + activeInstallations, err := p.multidevice.GetActiveInstallations(publicKey) + if err != nil { + return nil, err + } + + // Encrypt payload + directMessage, installations, err := p.encryptor.EncryptPayload(publicKey, myIdentityKey, activeInstallations, payload) + if err != nil { + return nil, err + } + + // Build message + message := &ProtocolMessage{ + InstallationId: p.encryptor.config.InstallationID, + DirectMessage: directMessage, + } + + err = p.addBundle(myIdentityKey, message, true) + if err != nil { + return nil, err + } + + // Check who we are sending the message to, and see if we have a shared secret + // across devices + var installationIDs []string + for installationID := range message.GetDirectMessage() { + if installationID != noInstallationID { + installationIDs = append(installationIDs, installationID) + } + } + + sharedSecret, agreed, err := p.secret.Agreed(myIdentityKey, p.encryptor.config.InstallationID, publicKey, installationIDs) + if err != nil { + return nil, err + } + + logger.Debug("shared secret agreement", + zap.Bool("has-shared-secret", sharedSecret != nil), + zap.Bool("agreed", agreed)) + + // Call handler + if sharedSecret != nil { + p.onNewSharedSecretHandler([]*sharedsecret.Secret{sharedSecret}) + } + + spec := &ProtocolMessageSpec{ + Message: message, + Installations: installations, + } + if agreed { + spec.SharedSecret = sharedSecret.Key + } + return spec, nil +} + +// BuildDHMessage builds a message with DH encryption so that it can be decrypted by any other device. +func (p *Protocol) BuildDHMessage(myIdentityKey *ecdsa.PrivateKey, destination *ecdsa.PublicKey, payload []byte) (*ProtocolMessageSpec, error) { + // Encrypt payload + encryptionResponse, err := p.encryptor.EncryptPayloadWithDH(destination, payload) + if err != nil { + return nil, err + } + + // Build message + message := &ProtocolMessage{ + InstallationId: p.encryptor.config.InstallationID, + DirectMessage: encryptionResponse, + } + + err = p.addBundle(myIdentityKey, message, true) + if err != nil { + return nil, err + } + + return &ProtocolMessageSpec{Message: message}, nil +} + +// ProcessPublicBundle processes a received X3DH bundle. +func (p *Protocol) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *Bundle) ([]*multidevice.Installation, error) { + logger := p.logger.With(zap.String("site", "ProcessPublicBundle")) + + logger.Debug("processing public bundle") + + if err := p.encryptor.ProcessPublicBundle(myIdentityKey, bundle); err != nil { + return nil, err + } + + installations, enabled, err := p.recoverInstallationsFromBundle(myIdentityKey, bundle) + if err != nil { + return nil, err + } + + logger.Debug("recovered installations", + zap.Int("installations", len(installations)), + zap.Bool("enabled", enabled)) + + // TODO(adam): why do we add installations using identity obtained from GetIdentity() + // instead of the output of crypto.CompressPubkey()? I tried the second option + // and the unit tests TestTopic and TestMaxDevices fail. + identityFromBundle := bundle.GetIdentity() + theirIdentity, err := ExtractIdentity(bundle) + if err != nil { + logger.Panic("unrecoverable error extracting identity", zap.Error(err)) + } + compressedIdentity := crypto.CompressPubkey(theirIdentity) + if !bytes.Equal(identityFromBundle, compressedIdentity) { + logger.Panic("identity from bundle and compressed are not equal") + } + + return p.multidevice.AddInstallations(bundle.GetIdentity(), bundle.GetTimestamp(), installations, enabled) +} + +// recoverInstallationsFromBundle extracts installations from the bundle. +// It returns extracted installations and true if the installations +// are ours, i.e. the bundle was created by our identity key. +func (p *Protocol) recoverInstallationsFromBundle(myIdentityKey *ecdsa.PrivateKey, bundle *Bundle) ([]*multidevice.Installation, bool, error) { + var installations []*multidevice.Installation + + theirIdentity, err := ExtractIdentity(bundle) + if err != nil { + return nil, false, err + } + + logger := p.logger.With(zap.String("site", "recoverInstallationsFromBundle")) + + myIdentityStr := fmt.Sprintf("0x%x", crypto.FromECDSAPub(&myIdentityKey.PublicKey)) + theirIdentityStr := fmt.Sprintf("0x%x", crypto.FromECDSAPub(theirIdentity)) + // Any device from other peers will be considered enabled, ours needs to + // be explicitly enabled. + enabled := theirIdentityStr != myIdentityStr + signedPreKeys := bundle.GetSignedPreKeys() + + for installationID, signedPreKey := range signedPreKeys { + logger.Info("recovered installation", zap.String("installation-id", installationID)) + if installationID != p.multidevice.InstallationID() { + installations = append(installations, &multidevice.Installation{ + Identity: theirIdentityStr, + ID: installationID, + Version: signedPreKey.GetProtocolVersion(), + }) + } + } + + return installations, enabled, nil +} + +// GetBundle retrieves or creates a X3DH bundle, given a private identity key. +func (p *Protocol) GetBundle(myIdentityKey *ecdsa.PrivateKey) (*Bundle, error) { + installations, err := p.multidevice.GetOurActiveInstallations(&myIdentityKey.PublicKey) + if err != nil { + return nil, err + } + + return p.encryptor.CreateBundle(myIdentityKey, installations) +} + +// EnableInstallation enables an installation for multi-device sync. +func (p *Protocol) EnableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error { + return p.multidevice.EnableInstallation(myIdentityKey, installationID) +} + +// DisableInstallation disables an installation for multi-device sync. +func (p *Protocol) DisableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error { + return p.multidevice.DisableInstallation(myIdentityKey, installationID) +} + +// GetOurInstallations returns all the installations available given an identity +func (p *Protocol) GetOurInstallations(myIdentityKey *ecdsa.PublicKey) ([]*multidevice.Installation, error) { + return p.multidevice.GetOurInstallations(myIdentityKey) +} + +// SetInstallationMetadata sets the metadata for our own installation +func (p *Protocol) SetInstallationMetadata(myIdentityKey *ecdsa.PublicKey, installationID string, data *multidevice.InstallationMetadata) error { + return p.multidevice.SetInstallationMetadata(myIdentityKey, installationID, data) +} + +// GetPublicBundle retrieves a public bundle given an identity +func (p *Protocol) GetPublicBundle(theirIdentityKey *ecdsa.PublicKey) (*Bundle, error) { + installations, err := p.multidevice.GetActiveInstallations(theirIdentityKey) + if err != nil { + return nil, err + } + return p.encryptor.GetPublicBundle(theirIdentityKey, installations) +} + +// ConfirmMessageProcessed confirms and deletes message keys for the given messages +func (p *Protocol) ConfirmMessageProcessed(messageID []byte) error { + return p.encryptor.ConfirmMessageProcessed(messageID) +} + +// HandleMessage unmarshals a message and processes it, decrypting it if it is a 1:1 message. +func (p *Protocol) HandleMessage( + myIdentityKey *ecdsa.PrivateKey, + theirPublicKey *ecdsa.PublicKey, + protocolMessage *ProtocolMessage, + messageID []byte, +) ([]byte, error) { + logger := p.logger.With(zap.String("site", "HandleMessage")) + + logger.Debug("received a protocol message", zap.Binary("sender-public-key", crypto.FromECDSAPub(theirPublicKey))) + + if p.encryptor == nil { + return nil, errors.New("encryption service not initialized") + } + + // Process bundle, deprecated, here for backward compatibility + if bundle := protocolMessage.GetBundle(); bundle != nil { + // Should we stop processing if the bundle cannot be verified? + addedBundles, err := p.ProcessPublicBundle(myIdentityKey, bundle) + if err != nil { + return nil, err + } + + p.onAddedBundlesHandler(addedBundles) + } + + // Process bundles + for _, bundle := range protocolMessage.GetBundles() { + // Should we stop processing if the bundle cannot be verified? + addedBundles, err := p.ProcessPublicBundle(myIdentityKey, bundle) + if err != nil { + return nil, err + } + + p.onAddedBundlesHandler(addedBundles) + } + + // Check if it's a public message + if publicMessage := protocolMessage.GetPublicMessage(); publicMessage != nil { + logger.Debug("received a public message in direct message") + // Nothing to do, as already in cleartext + return publicMessage, nil + } + + // Decrypt message + if directMessage := protocolMessage.GetDirectMessage(); directMessage != nil { + logger.Debug("processing direct message") + message, err := p.encryptor.DecryptPayload( + myIdentityKey, + theirPublicKey, + protocolMessage.GetInstallationId(), + directMessage, + messageID, + ) + if err != nil { + return nil, err + } + + // Handle protocol negotiation for compatible clients + bundles := append(protocolMessage.GetBundles(), protocolMessage.GetBundle()) + version := getProtocolVersion(bundles, protocolMessage.GetInstallationId()) + logger.Debug("direct message version", zap.Uint32("version", version)) + if version >= sharedSecretNegotiationVersion { + logger.Debug("negotiating shared secret", + zap.Binary("public-key", crypto.FromECDSAPub(theirPublicKey))) + sharedSecret, err := p.secret.Generate(myIdentityKey, theirPublicKey, protocolMessage.GetInstallationId()) + if err != nil { + return nil, err + } + + p.onNewSharedSecretHandler([]*sharedsecret.Secret{sharedSecret}) + } + return message, nil + } + + // Return error + return nil, ErrNoPayload +} + +func (p *Protocol) ShouldAdvertiseBundle(publicKey *ecdsa.PublicKey, time int64) (bool, error) { + return p.publisher.ShouldAdvertiseBundle(publicKey, time) +} + +func (p *Protocol) ConfirmBundleAdvertisement(publicKey *ecdsa.PublicKey, time int64) { + p.publisher.SetLastAck(publicKey, time) +} + +func (p *Protocol) BuildBundleAdvertiseMessage(myIdentityKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey) (*ProtocolMessageSpec, error) { + return p.BuildDHMessage(myIdentityKey, publicKey, nil) +} + +func getProtocolVersion(bundles []*Bundle, installationID string) uint32 { + if installationID == "" { + return defaultMinVersion + } + + for _, bundle := range bundles { + if bundle != nil { + signedPreKeys := bundle.GetSignedPreKeys() + if signedPreKeys == nil { + continue + } + + signedPreKey := signedPreKeys[installationID] + if signedPreKey == nil { + return defaultMinVersion + } + + return signedPreKey.GetProtocolVersion() + } + } + + return defaultMinVersion +} diff --git a/messaging/chat/protobuf/encryption.pb.go b/vendor/github.com/status-im/status-protocol-go/encryption/protocol_message.pb.go similarity index 75% rename from messaging/chat/protobuf/encryption.pb.go rename to vendor/github.com/status-im/status-protocol-go/encryption/protocol_message.pb.go index 760de5cdc..7ed69bc88 100644 --- a/messaging/chat/protobuf/encryption.pb.go +++ b/vendor/github.com/status-im/status-protocol-go/encryption/protocol_message.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. -// source: encryption.proto +// source: protocol_message.proto -package protobuf +package encryption import ( fmt "fmt" @@ -18,7 +18,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type SignedPreKey struct { SignedPreKey []byte `protobuf:"bytes,1,opt,name=signed_pre_key,json=signedPreKey,proto3" json:"signed_pre_key,omitempty"` @@ -33,7 +33,7 @@ func (m *SignedPreKey) Reset() { *m = SignedPreKey{} } func (m *SignedPreKey) String() string { return proto.CompactTextString(m) } func (*SignedPreKey) ProtoMessage() {} func (*SignedPreKey) Descriptor() ([]byte, []int) { - return fileDescriptor_8293a649ce9418c6, []int{0} + return fileDescriptor_4e37b52004a72e16, []int{0} } func (m *SignedPreKey) XXX_Unmarshal(b []byte) error { @@ -94,7 +94,7 @@ func (m *Bundle) Reset() { *m = Bundle{} } func (m *Bundle) String() string { return proto.CompactTextString(m) } func (*Bundle) ProtoMessage() {} func (*Bundle) Descriptor() ([]byte, []int) { - return fileDescriptor_8293a649ce9418c6, []int{1} + return fileDescriptor_4e37b52004a72e16, []int{1} } func (m *Bundle) XXX_Unmarshal(b []byte) error { @@ -157,7 +157,7 @@ func (m *BundleContainer) Reset() { *m = BundleContainer{} } func (m *BundleContainer) String() string { return proto.CompactTextString(m) } func (*BundleContainer) ProtoMessage() {} func (*BundleContainer) Descriptor() ([]byte, []int) { - return fileDescriptor_8293a649ce9418c6, []int{2} + return fileDescriptor_4e37b52004a72e16, []int{2} } func (m *BundleContainer) XXX_Unmarshal(b []byte) error { @@ -210,7 +210,7 @@ func (m *DRHeader) Reset() { *m = DRHeader{} } func (m *DRHeader) String() string { return proto.CompactTextString(m) } func (*DRHeader) ProtoMessage() {} func (*DRHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_8293a649ce9418c6, []int{3} + return fileDescriptor_4e37b52004a72e16, []int{3} } func (m *DRHeader) XXX_Unmarshal(b []byte) error { @@ -271,7 +271,7 @@ func (m *DHHeader) Reset() { *m = DHHeader{} } func (m *DHHeader) String() string { return proto.CompactTextString(m) } func (*DHHeader) ProtoMessage() {} func (*DHHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_8293a649ce9418c6, []int{4} + return fileDescriptor_4e37b52004a72e16, []int{4} } func (m *DHHeader) XXX_Unmarshal(b []byte) error { @@ -313,7 +313,7 @@ func (m *X3DHHeader) Reset() { *m = X3DHHeader{} } func (m *X3DHHeader) String() string { return proto.CompactTextString(m) } func (*X3DHHeader) ProtoMessage() {} func (*X3DHHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_8293a649ce9418c6, []int{5} + return fileDescriptor_4e37b52004a72e16, []int{5} } func (m *X3DHHeader) XXX_Unmarshal(b []byte) error { @@ -364,7 +364,7 @@ func (m *DirectMessageProtocol) Reset() { *m = DirectMessageProtocol{} } func (m *DirectMessageProtocol) String() string { return proto.CompactTextString(m) } func (*DirectMessageProtocol) ProtoMessage() {} func (*DirectMessageProtocol) Descriptor() ([]byte, []int) { - return fileDescriptor_8293a649ce9418c6, []int{6} + return fileDescriptor_4e37b52004a72e16, []int{6} } func (m *DirectMessageProtocol) XXX_Unmarshal(b []byte) error { @@ -434,7 +434,7 @@ func (m *ProtocolMessage) Reset() { *m = ProtocolMessage{} } func (m *ProtocolMessage) String() string { return proto.CompactTextString(m) } func (*ProtocolMessage) ProtoMessage() {} func (*ProtocolMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_8293a649ce9418c6, []int{7} + return fileDescriptor_4e37b52004a72e16, []int{7} } func (m *ProtocolMessage) XXX_Unmarshal(b []byte) error { @@ -491,56 +491,56 @@ func (m *ProtocolMessage) GetPublicMessage() []byte { } func init() { - proto.RegisterType((*SignedPreKey)(nil), "protobuf.SignedPreKey") - proto.RegisterType((*Bundle)(nil), "protobuf.Bundle") - proto.RegisterMapType((map[string]*SignedPreKey)(nil), "protobuf.Bundle.SignedPreKeysEntry") - proto.RegisterType((*BundleContainer)(nil), "protobuf.BundleContainer") - proto.RegisterType((*DRHeader)(nil), "protobuf.DRHeader") - proto.RegisterType((*DHHeader)(nil), "protobuf.DHHeader") - proto.RegisterType((*X3DHHeader)(nil), "protobuf.X3DHHeader") - proto.RegisterType((*DirectMessageProtocol)(nil), "protobuf.DirectMessageProtocol") - proto.RegisterType((*ProtocolMessage)(nil), "protobuf.ProtocolMessage") - proto.RegisterMapType((map[string]*DirectMessageProtocol)(nil), "protobuf.ProtocolMessage.DirectMessageEntry") + proto.RegisterType((*SignedPreKey)(nil), "encryption.SignedPreKey") + proto.RegisterType((*Bundle)(nil), "encryption.Bundle") + proto.RegisterMapType((map[string]*SignedPreKey)(nil), "encryption.Bundle.SignedPreKeysEntry") + proto.RegisterType((*BundleContainer)(nil), "encryption.BundleContainer") + proto.RegisterType((*DRHeader)(nil), "encryption.DRHeader") + proto.RegisterType((*DHHeader)(nil), "encryption.DHHeader") + proto.RegisterType((*X3DHHeader)(nil), "encryption.X3DHHeader") + proto.RegisterType((*DirectMessageProtocol)(nil), "encryption.DirectMessageProtocol") + proto.RegisterType((*ProtocolMessage)(nil), "encryption.ProtocolMessage") + proto.RegisterMapType((map[string]*DirectMessageProtocol)(nil), "encryption.ProtocolMessage.DirectMessageEntry") } -func init() { proto.RegisterFile("encryption.proto", fileDescriptor_8293a649ce9418c6) } +func init() { proto.RegisterFile("protocol_message.proto", fileDescriptor_4e37b52004a72e16) } -var fileDescriptor_8293a649ce9418c6 = []byte{ - // 566 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xdd, 0x6a, 0xdb, 0x4c, - 0x10, 0x45, 0x52, 0xe2, 0x9f, 0xb1, 0xfc, 0xc3, 0x7e, 0x5f, 0x83, 0x30, 0x81, 0x1a, 0xb5, 0xa5, - 0x6e, 0x09, 0x2e, 0xd8, 0x0d, 0x94, 0x5e, 0xb6, 0x2e, 0xb8, 0x09, 0x85, 0xb0, 0x81, 0x92, 0x3b, - 0xb1, 0xb6, 0x36, 0xe9, 0x52, 0x79, 0x25, 0x76, 0xd7, 0x06, 0x3d, 0x41, 0xdf, 0xad, 0x2f, 0xd3, - 0x57, 0x28, 0x5a, 0x69, 0xad, 0xb5, 0x9d, 0x5c, 0xf4, 0xca, 0x9e, 0xb3, 0x73, 0xce, 0xcc, 0x9c, - 0xd1, 0xc0, 0x80, 0xf2, 0x95, 0xc8, 0x33, 0xc5, 0x52, 0x3e, 0xc9, 0x44, 0xaa, 0x52, 0xd4, 0xd2, - 0x3f, 0xcb, 0xcd, 0x7d, 0x98, 0x83, 0x7f, 0xcb, 0x1e, 0x38, 0x8d, 0x6f, 0x04, 0xbd, 0xa6, 0x39, - 0x7a, 0x09, 0x3d, 0xa9, 0xe3, 0x28, 0x13, 0x34, 0xfa, 0x49, 0xf3, 0xc0, 0x19, 0x39, 0x63, 0x1f, - 0xfb, 0xd2, 0xce, 0x0a, 0xa0, 0xb9, 0xa5, 0x42, 0xb2, 0x94, 0x07, 0xee, 0xc8, 0x19, 0x77, 0xb1, - 0x09, 0xd1, 0x1b, 0x18, 0x68, 0xed, 0x55, 0x9a, 0x44, 0x26, 0xc5, 0xd3, 0x29, 0x7d, 0x83, 0x7f, - 0x2f, 0xe1, 0xf0, 0x97, 0x0b, 0x8d, 0x4f, 0x1b, 0x1e, 0x27, 0x14, 0x0d, 0xa1, 0xc5, 0x62, 0xca, - 0x15, 0x53, 0xa6, 0xde, 0x2e, 0x46, 0xd7, 0xd0, 0xdf, 0xef, 0x48, 0x06, 0xee, 0xc8, 0x1b, 0x77, - 0xa6, 0x2f, 0x26, 0x66, 0x8a, 0x49, 0x29, 0x33, 0xb1, 0x27, 0x91, 0x5f, 0xb8, 0x12, 0x39, 0xee, - 0xda, 0x7d, 0x4b, 0x74, 0x0e, 0xed, 0x02, 0x20, 0x6a, 0x23, 0x68, 0x70, 0xa2, 0x2b, 0xd5, 0x40, - 0xf1, 0xaa, 0xd8, 0x9a, 0x4a, 0x45, 0xd6, 0x59, 0x70, 0x3a, 0x72, 0xc6, 0x1e, 0xae, 0x81, 0xe1, - 0x1d, 0xa0, 0xe3, 0x02, 0x68, 0x00, 0x9e, 0x71, 0xa9, 0x8d, 0x8b, 0xbf, 0xe8, 0x02, 0x4e, 0xb7, - 0x24, 0xd9, 0x50, 0x6d, 0x4d, 0x67, 0x7a, 0x56, 0xb7, 0x69, 0xd3, 0x71, 0x99, 0xf4, 0xd1, 0xfd, - 0xe0, 0x84, 0x5b, 0xe8, 0x97, 0x13, 0x7c, 0x4e, 0xb9, 0x22, 0x8c, 0x53, 0x81, 0xc6, 0xd0, 0x58, - 0x6a, 0x48, 0x2b, 0x77, 0xa6, 0x83, 0xc3, 0x61, 0x71, 0xf5, 0x8e, 0x66, 0x70, 0x96, 0x09, 0xb6, - 0x25, 0x8a, 0x46, 0x07, 0x9b, 0x73, 0xf5, 0x7c, 0xff, 0x55, 0xaf, 0x76, 0xf1, 0xab, 0x93, 0x96, - 0x37, 0x38, 0x09, 0xaf, 0xa0, 0x35, 0xc7, 0x0b, 0x4a, 0x62, 0x2a, 0xec, 0x39, 0xfc, 0x72, 0x0e, - 0x1f, 0x1c, 0xb3, 0x5e, 0x87, 0xa3, 0x1e, 0xb8, 0x99, 0x59, 0xa5, 0x9b, 0xe9, 0x98, 0xc5, 0x95, - 0x85, 0x2e, 0x8b, 0xc3, 0x73, 0x68, 0xcd, 0x17, 0x4f, 0x69, 0x85, 0xef, 0x01, 0xee, 0x66, 0x4f, - 0xbf, 0x1f, 0xaa, 0x55, 0xfd, 0xfd, 0x76, 0xe0, 0xd9, 0x9c, 0x09, 0xba, 0x52, 0xdf, 0xa8, 0x94, - 0xe4, 0x81, 0xde, 0x54, 0x9f, 0x10, 0xba, 0x84, 0x4e, 0xa1, 0x17, 0xfd, 0xd0, 0x82, 0x95, 0x47, - 0xff, 0xd7, 0x1e, 0xd5, 0xc5, 0xb0, 0x5d, 0xf8, 0x1d, 0xb4, 0xe7, 0xd8, 0x90, 0xca, 0xf5, 0xa0, - 0x9a, 0x64, 0xbc, 0xc0, 0xb5, 0x2b, 0x05, 0x61, 0x57, 0x85, 0x1e, 0x11, 0x16, 0x3b, 0x82, 0xa9, - 0x10, 0x40, 0x33, 0x23, 0x79, 0x92, 0x92, 0x58, 0x7b, 0xe5, 0x63, 0x13, 0x86, 0x7f, 0x5c, 0xe8, - 0x9b, 0xfe, 0xab, 0x71, 0xfe, 0x61, 0xcb, 0xaf, 0xa1, 0xcf, 0xb8, 0x54, 0x24, 0x49, 0x48, 0x71, - 0xc7, 0x11, 0x8b, 0x75, 0xff, 0x6d, 0xdc, 0xb3, 0xe1, 0xaf, 0x31, 0x7a, 0x0b, 0xcd, 0x92, 0x22, - 0x03, 0x4f, 0x9f, 0xc9, 0xb1, 0xa6, 0x49, 0x40, 0xb7, 0xd0, 0x8b, 0xb5, 0xbd, 0xd1, 0xba, 0x6c, - 0x28, 0xa0, 0x9a, 0x72, 0x51, 0x53, 0x0e, 0x3a, 0x9e, 0xec, 0xad, 0xa3, 0x3a, 0xb1, 0xd8, 0xc6, - 0xd0, 0x2b, 0xe8, 0x65, 0x9b, 0x65, 0xc2, 0x56, 0x3b, 0xd1, 0x7b, 0x6d, 0x44, 0xb7, 0x44, 0xab, - 0xb4, 0x21, 0x01, 0x74, 0xac, 0xf5, 0xc8, 0x35, 0x5d, 0xee, 0x5f, 0xd3, 0x73, 0xcb, 0xfd, 0xc7, - 0xbe, 0x0c, 0xeb, 0xac, 0x96, 0x0d, 0x9d, 0x3a, 0xfb, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xcd, 0x2d, - 0x0e, 0xc8, 0x00, 0x05, 0x00, 0x00, +var fileDescriptor_4e37b52004a72e16 = []byte{ + // 565 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x5d, 0x8b, 0xd3, 0x40, + 0x14, 0x25, 0xc9, 0xee, 0xb6, 0xbd, 0x4d, 0x3f, 0x18, 0x75, 0x09, 0x65, 0x1f, 0x6a, 0x70, 0xb1, + 0x8a, 0x04, 0x6c, 0x85, 0x15, 0x1f, 0xb5, 0x42, 0x5d, 0x59, 0x58, 0x46, 0x14, 0xf1, 0x25, 0x4c, + 0x3b, 0xe3, 0x3a, 0x98, 0x4e, 0x42, 0x66, 0x5a, 0xcc, 0x6f, 0xf0, 0xcf, 0xf9, 0x6f, 0x7c, 0x95, + 0x4c, 0x32, 0xed, 0xf4, 0x63, 0x1f, 0x7c, 0xcb, 0x9c, 0xb9, 0xf7, 0xdc, 0x7b, 0xce, 0x9d, 0x1b, + 0x38, 0xcf, 0xf2, 0x54, 0xa5, 0x8b, 0x34, 0x89, 0x97, 0x4c, 0x4a, 0x72, 0xc7, 0x22, 0x0d, 0x20, + 0x60, 0x62, 0x91, 0x17, 0x99, 0xe2, 0xa9, 0x08, 0x0b, 0xf0, 0x3f, 0xf1, 0x3b, 0xc1, 0xe8, 0x6d, + 0xce, 0x3e, 0xb2, 0x02, 0x3d, 0x81, 0xae, 0xd4, 0xe7, 0x38, 0xcb, 0x59, 0xfc, 0x93, 0x15, 0x81, + 0x33, 0x74, 0x46, 0x3e, 0xf6, 0xa5, 0x1d, 0x15, 0x40, 0x63, 0xcd, 0x72, 0xc9, 0x53, 0x11, 0xb8, + 0x43, 0x67, 0xd4, 0xc1, 0xe6, 0x88, 0x9e, 0x41, 0x7f, 0x53, 0xd5, 0x84, 0x78, 0x3a, 0xa4, 0x67, + 0xf0, 0x2f, 0x15, 0x1c, 0xfe, 0x76, 0xe1, 0xec, 0xed, 0x4a, 0xd0, 0x84, 0xa1, 0x01, 0x34, 0x39, + 0x65, 0x42, 0x71, 0x65, 0xea, 0x6d, 0xce, 0xe8, 0x06, 0x7a, 0xbb, 0x1d, 0xc9, 0xc0, 0x1d, 0x7a, + 0xa3, 0xf6, 0xf8, 0x32, 0xda, 0xea, 0x88, 0x2a, 0xa2, 0xc8, 0xd6, 0x22, 0xdf, 0x0b, 0x95, 0x17, + 0xb8, 0x63, 0x77, 0x2e, 0xd1, 0x05, 0xb4, 0x4a, 0x80, 0xa8, 0x55, 0xce, 0x82, 0x13, 0x5d, 0x6b, + 0x0b, 0x94, 0xb7, 0x8a, 0x2f, 0x99, 0x54, 0x64, 0x99, 0x05, 0xa7, 0x43, 0x67, 0xe4, 0xe1, 0x2d, + 0x30, 0xf8, 0x06, 0xe8, 0xb0, 0x00, 0xea, 0x83, 0x67, 0x7c, 0x6a, 0xe1, 0xf2, 0x13, 0x45, 0x70, + 0xba, 0x26, 0xc9, 0x8a, 0x69, 0x73, 0xda, 0xe3, 0xc0, 0x6e, 0xd4, 0x26, 0xc0, 0x55, 0xd8, 0x1b, + 0xf7, 0xb5, 0x13, 0xfe, 0x82, 0x5e, 0xa5, 0xe1, 0x5d, 0x2a, 0x14, 0xe1, 0x82, 0xe5, 0xe8, 0x39, + 0x9c, 0xcd, 0x35, 0xa4, 0xb9, 0xdb, 0x63, 0x74, 0x28, 0x18, 0xd7, 0x11, 0x68, 0x52, 0x4e, 0x9b, + 0xaf, 0x89, 0x62, 0xf1, 0xde, 0xfc, 0x5c, 0xad, 0xf1, 0x41, 0x7d, 0x6b, 0x97, 0xbf, 0x3e, 0x69, + 0x7a, 0xfd, 0x93, 0xf0, 0x1a, 0x9a, 0x53, 0x3c, 0x63, 0x84, 0xb2, 0xdc, 0xd6, 0xe2, 0x57, 0x5a, + 0x7c, 0x70, 0xcc, 0x90, 0x1d, 0x81, 0xba, 0xe0, 0x66, 0x66, 0xa0, 0x6e, 0xa6, 0xcf, 0x9c, 0xd6, + 0x36, 0xba, 0x9c, 0x86, 0x17, 0xd0, 0x9c, 0xce, 0xee, 0xe3, 0x0a, 0x5f, 0x01, 0x7c, 0x9d, 0xdc, + 0x7f, 0xbf, 0xcf, 0x56, 0xf7, 0xf7, 0xc7, 0x81, 0x47, 0x53, 0x9e, 0xb3, 0x85, 0xba, 0xa9, 0x9e, + 0xf1, 0x6d, 0xfd, 0x90, 0xd0, 0x15, 0xb4, 0x4b, 0xbe, 0xf8, 0x87, 0x26, 0xac, 0x5d, 0x3a, 0xb7, + 0x5d, 0xda, 0x96, 0xc3, 0x76, 0xe9, 0x97, 0xd0, 0x9a, 0x62, 0x93, 0x56, 0x0d, 0xe9, 0xa1, 0x9d, + 0x66, 0xfc, 0xc0, 0x5b, 0x67, 0xca, 0x94, 0x4d, 0x25, 0x76, 0x24, 0x65, 0xb6, 0x49, 0x31, 0x55, + 0x02, 0x68, 0x64, 0xa4, 0x48, 0x52, 0x42, 0xb5, 0x63, 0x3e, 0x36, 0xc7, 0xf0, 0xaf, 0x0b, 0x3d, + 0xa3, 0xa2, 0x16, 0xf5, 0x5f, 0xd3, 0x7e, 0x0a, 0x3d, 0x2e, 0xa4, 0x22, 0x49, 0x42, 0xca, 0xeb, + 0x98, 0x53, 0xad, 0xa2, 0x85, 0xbb, 0x36, 0xfc, 0x81, 0xa2, 0x17, 0xd0, 0xa8, 0x52, 0x64, 0xe0, + 0xe9, 0xa5, 0x39, 0xc6, 0x6a, 0x42, 0xd0, 0x67, 0xe8, 0x52, 0x6d, 0xb4, 0xf9, 0x61, 0x04, 0x4c, + 0x27, 0x45, 0x76, 0xd2, 0x5e, 0xdf, 0xd1, 0xce, 0x68, 0xea, 0x95, 0xa3, 0x36, 0x86, 0x2e, 0xa1, + 0x9b, 0xad, 0xe6, 0x09, 0x5f, 0x6c, 0x68, 0xbf, 0x6b, 0x3b, 0x3a, 0x15, 0x5a, 0x87, 0x0d, 0x16, + 0x80, 0x0e, 0xb9, 0x8e, 0x6c, 0xd7, 0xd5, 0xee, 0x76, 0x3d, 0xde, 0x99, 0xc2, 0xb1, 0x77, 0x62, + 0xad, 0xd9, 0xfc, 0x4c, 0xff, 0x85, 0x26, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0xb6, 0xd6, 0x74, + 0x9a, 0x1c, 0x05, 0x00, 0x00, } diff --git a/messaging/chat/protobuf/encryption.proto b/vendor/github.com/status-im/status-protocol-go/encryption/protocol_message.proto similarity index 98% rename from messaging/chat/protobuf/encryption.proto rename to vendor/github.com/status-im/status-protocol-go/encryption/protocol_message.proto index c8b10277d..0f3e35d56 100644 --- a/messaging/chat/protobuf/encryption.proto +++ b/vendor/github.com/status-im/status-protocol-go/encryption/protocol_message.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package protobuf; +package encryption; message SignedPreKey { bytes signed_pre_key = 1; diff --git a/vendor/github.com/status-im/status-protocol-go/encryption/publisher/doc.go b/vendor/github.com/status-im/status-protocol-go/encryption/publisher/doc.go new file mode 100644 index 000000000..3d317e435 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/encryption/publisher/doc.go @@ -0,0 +1,6 @@ +// Publisher periodically publishes an info about itself on a known channel. +// This channel is a particular topic calculated from the public key. +// It is required for other peers to start a secure conversation immediately +// using distibuted data through the channel. + +package publisher diff --git a/vendor/github.com/status-im/status-protocol-go/encryption/publisher/persistence.go b/vendor/github.com/status-im/status-protocol-go/encryption/publisher/persistence.go new file mode 100644 index 000000000..5452f86f1 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/encryption/publisher/persistence.go @@ -0,0 +1,54 @@ +package publisher + +import ( + "database/sql" + "encoding/hex" + "sync" +) + +type sqlitePersistence struct { + db *sql.DB + lastAcksMutex sync.Mutex + lastAcks map[string]int64 +} + +func newSQLitePersistence(db *sql.DB) *sqlitePersistence { + return &sqlitePersistence{ + db: db, + lastAcks: make(map[string]int64), + } +} + +func (s *sqlitePersistence) lastPublished() (int64, error) { + var lastPublished int64 + statement := "SELECT last_published FROM contact_code_config LIMIT 1" + err := s.db.QueryRow(statement).Scan(&lastPublished) + if err != nil { + return 0, err + } + return lastPublished, nil +} + +func (s *sqlitePersistence) setLastPublished(lastPublished int64) error { + statement := "UPDATE contact_code_config SET last_published = ?" + stmt, err := s.db.Prepare(statement) + if err != nil { + return err + } + defer stmt.Close() + + _, err = stmt.Exec(lastPublished) + return err +} + +func (s *sqlitePersistence) lastAck(identity []byte) (int64, error) { + s.lastAcksMutex.Lock() + defer s.lastAcksMutex.Unlock() + return s.lastAcks[hex.EncodeToString(identity)], nil +} + +func (s *sqlitePersistence) setLastAck(identity []byte, lastAck int64) { + s.lastAcksMutex.Lock() + defer s.lastAcksMutex.Unlock() + s.lastAcks[hex.EncodeToString(identity)] = lastAck +} diff --git a/vendor/github.com/status-im/status-protocol-go/encryption/publisher/publisher.go b/vendor/github.com/status-im/status-protocol-go/encryption/publisher/publisher.go new file mode 100644 index 000000000..8043600d5 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/encryption/publisher/publisher.go @@ -0,0 +1,124 @@ +package publisher + +import ( + "crypto/ecdsa" + "database/sql" + "errors" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "go.uber.org/zap" +) + +const ( + // How often a ticker fires in seconds. + tickerInterval = 120 + // How often we should publish a contact code in seconds. + publishInterval = 21600 + // Cooldown period on acking messages when not targeting our device. + deviceNotFoundAckInterval = 7200 +) + +var ( + errNotEnoughTimePassed = errors.New("not enough time passed") +) + +type Publisher struct { + persistence *sqlitePersistence + logger *zap.Logger + notifyCh chan struct{} + quit chan struct{} +} + +func New(db *sql.DB, logger *zap.Logger) *Publisher { + if logger == nil { + logger = zap.NewNop() + } + + return &Publisher{ + persistence: newSQLitePersistence(db), + logger: logger.With(zap.Namespace("Publisher")), + } +} + +func (p *Publisher) Start() <-chan struct{} { + logger := p.logger.With(zap.String("site", "Start")) + + logger.Info("starting publisher") + + p.notifyCh = make(chan struct{}) + p.quit = make(chan struct{}) + + go p.tickerLoop() + + return p.notifyCh +} + +func (p *Publisher) Stop() { + select { + case _, ok := <-p.quit: + if !ok { + // channel already closed + return + } + default: + close(p.quit) + } +} + +func (p *Publisher) tickerLoop() { + ticker := time.NewTicker(tickerInterval * time.Second) + + go func() { + logger := p.logger.With(zap.String("site", "tickerLoop")) + + for { + select { + case <-ticker.C: + err := p.notify() + switch err { + case errNotEnoughTimePassed: + logger.Debug("not enough time passed") + case nil: + // skip + default: + logger.Error("error while sending a contact code", zap.Error(err)) + } + case <-p.quit: + ticker.Stop() + return + } + } + }() +} + +func (p *Publisher) notify() error { + lastPublished, err := p.persistence.lastPublished() + if err != nil { + return err + } + + now := time.Now().Unix() + + if now-lastPublished < publishInterval { + return errNotEnoughTimePassed + } + + p.notifyCh <- struct{}{} + + return p.persistence.setLastPublished(now) +} + +func (p *Publisher) ShouldAdvertiseBundle(publicKey *ecdsa.PublicKey, now int64) (bool, error) { + identity := crypto.CompressPubkey(publicKey) + lastAcked, err := p.persistence.lastAck(identity) + if err != nil { + return false, err + } + return now-lastAcked < deviceNotFoundAckInterval, nil +} + +func (p *Publisher) SetLastAck(publicKey *ecdsa.PublicKey, now int64) { + identity := crypto.CompressPubkey(publicKey) + p.persistence.setLastAck(identity, now) +} diff --git a/messaging/sharedsecret/persistence.go b/vendor/github.com/status-im/status-protocol-go/encryption/sharedsecret/persistence.go similarity index 62% rename from messaging/sharedsecret/persistence.go rename to vendor/github.com/status-im/status-protocol-go/encryption/sharedsecret/persistence.go index 29a97c4cc..701070752 100644 --- a/messaging/sharedsecret/persistence.go +++ b/vendor/github.com/status-im/status-protocol-go/encryption/sharedsecret/persistence.go @@ -5,30 +5,20 @@ import ( "strings" ) -type Persistence interface { - // Add adds a shared secret, associated with an identity and an installationID - Add(identity []byte, secret []byte, installationID string) error - // Get returns a shared secret associated with multiple installationIDs - Get(identity []byte, installationIDs []string) (*Response, error) - // All returns an array of shared secrets, each one of them represented - // as a byte array - All() ([][][]byte, error) -} - type Response struct { secret []byte installationIDs map[string]bool } -type SQLLitePersistence struct { +type sqlitePersistence struct { db *sql.DB } -func NewSQLLitePersistence(db *sql.DB) *SQLLitePersistence { - return &SQLLitePersistence{db: db} +func newSQLitePersistence(db *sql.DB) *sqlitePersistence { + return &sqlitePersistence{db: db} } -func (s *SQLLitePersistence) Add(identity []byte, secret []byte, installationID string) error { +func (s *sqlitePersistence) Add(identity []byte, secret []byte, installationID string) error { tx, err := s.db.Begin() if err != nil { return err @@ -62,7 +52,7 @@ func (s *SQLLitePersistence) Add(identity []byte, secret []byte, installationID return tx.Commit() } -func (s *SQLLitePersistence) Get(identity []byte, installationIDs []string) (*Response, error) { +func (s *sqlitePersistence) Get(identity []byte, installationIDs []string) (*Response, error) { response := &Response{ installationIDs: make(map[string]bool), } @@ -74,14 +64,14 @@ func (s *SQLLitePersistence) Get(identity []byte, installationIDs []string) (*Re /* #nosec */ query := `SELECT secret, id - FROM secrets t - JOIN - secret_installation_ids tid - ON t.identity = tid.identity_id - WHERE - t.identity = ? - AND - tid.id IN (?` + strings.Repeat(",?", len(installationIDs)-1) + `)` + FROM secrets t + JOIN + secret_installation_ids tid + ON t.identity = tid.identity_id + WHERE + t.identity = ? + AND + tid.id IN (?` + strings.Repeat(",?", len(installationIDs)-1) + `)` rows, err := s.db.Query(query, args...) if err != nil && err != sql.ErrNoRows { @@ -103,9 +93,8 @@ func (s *SQLLitePersistence) Get(identity []byte, installationIDs []string) (*Re return response, nil } -func (s *SQLLitePersistence) All() ([][][]byte, error) { - query := `SELECT identity, secret - FROM secrets` +func (s *sqlitePersistence) All() ([][][]byte, error) { + query := "SELECT identity, secret FROM secrets" var secrets [][][]byte diff --git a/vendor/github.com/status-im/status-protocol-go/encryption/sharedsecret/sharedsecret.go b/vendor/github.com/status-im/status-protocol-go/encryption/sharedsecret/sharedsecret.go new file mode 100644 index 000000000..aa7c4022a --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/encryption/sharedsecret/sharedsecret.go @@ -0,0 +1,127 @@ +package sharedsecret + +import ( + "bytes" + "crypto/ecdsa" + "database/sql" + "errors" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/ecies" + "go.uber.org/zap" +) + +const sskLen = 16 + +type Secret struct { + Identity *ecdsa.PublicKey + Key []byte +} + +// SharedSecret generates and manages negotiated secrets. +// Identities (public keys) stored by SharedSecret +// are compressed. +// TODO: make compression of public keys a responsibility of sqlitePersistence instead of SharedSecret. +type SharedSecret struct { + persistence *sqlitePersistence + logger *zap.Logger +} + +func New(db *sql.DB, logger *zap.Logger) *SharedSecret { + if logger == nil { + logger = zap.NewNop() + } + + return &SharedSecret{ + persistence: newSQLitePersistence(db), + logger: logger.With(zap.Namespace("SharedSecret")), + } +} + +func (s *SharedSecret) generate(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, installationID string) (*Secret, error) { + sharedKey, err := ecies.ImportECDSA(myPrivateKey).GenerateShared( + ecies.ImportECDSAPublic(theirPublicKey), + sskLen, + sskLen, + ) + if err != nil { + return nil, err + } + + logger := s.logger.With(zap.String("site", "generate")) + + logger.Debug( + "saving a shared key", + zap.Binary("their-public-key", crypto.FromECDSAPub(theirPublicKey)), + zap.String("installation-id", installationID), + ) + + theirIdentity := crypto.CompressPubkey(theirPublicKey) + if err = s.persistence.Add(theirIdentity, sharedKey, installationID); err != nil { + return nil, err + } + + return &Secret{Key: sharedKey, Identity: theirPublicKey}, err +} + +// Generate will generate a shared secret for a given identity, and return it. +func (s *SharedSecret) Generate(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, installationID string) (*Secret, error) { + return s.generate(myPrivateKey, theirPublicKey, installationID) +} + +// Agreed returns true if a secret has been acknowledged by all the installationIDs. +func (s *SharedSecret) Agreed(myPrivateKey *ecdsa.PrivateKey, myInstallationID string, theirPublicKey *ecdsa.PublicKey, theirInstallationIDs []string) (*Secret, bool, error) { + logger := s.logger.With(zap.String("site", "Agreed")) + + logger.Debug( + "checking if shared secret is acknowledged", + zap.Binary("their-public-key", crypto.FromECDSAPub(theirPublicKey)), + zap.Strings("their-installation-ids", theirInstallationIDs), + ) + + secret, err := s.generate(myPrivateKey, theirPublicKey, myInstallationID) + if err != nil { + return nil, false, err + } + + if len(theirInstallationIDs) == 0 { + return secret, false, nil + } + + theirIdentity := crypto.CompressPubkey(theirPublicKey) + response, err := s.persistence.Get(theirIdentity, theirInstallationIDs) + if err != nil { + return nil, false, err + } + + for _, installationID := range theirInstallationIDs { + if !response.installationIDs[installationID] { + logger.Debug("no shared secret for installation", zap.String("installation-id", installationID)) + return secret, false, nil + } + } + + if !bytes.Equal(secret.Key, response.secret) { + return nil, false, errors.New("computed and saved secrets are different for a given identity") + } + + return secret, true, nil +} + +func (s *SharedSecret) All() ([]*Secret, error) { + var secrets []*Secret + tuples, err := s.persistence.All() + if err != nil { + return nil, err + } + + for _, tuple := range tuples { + key, err := crypto.DecompressPubkey(tuple[0]) + if err != nil { + return nil, err + } + secrets = append(secrets, &Secret{Identity: key, Key: tuple[1]}) + } + + return secrets, nil +} diff --git a/messaging/chat/x3dh.go b/vendor/github.com/status-im/status-protocol-go/encryption/x3dh.go similarity index 91% rename from messaging/chat/x3dh.go rename to vendor/github.com/status-im/status-protocol-go/encryption/x3dh.go index 2e3c517e0..cafe91b2c 100644 --- a/messaging/chat/x3dh.go +++ b/vendor/github.com/status-im/status-protocol-go/encryption/x3dh.go @@ -1,4 +1,4 @@ -package chat +package encryption import ( "crypto/ecdsa" @@ -9,7 +9,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/ecies" - "github.com/status-im/status-go/messaging/chat/protobuf" ) const ( @@ -17,7 +16,7 @@ const ( sskLen = 16 ) -func buildSignatureMaterial(bundle *protobuf.Bundle) []byte { +func buildSignatureMaterial(bundle *Bundle) []byte { signedPreKeys := bundle.GetSignedPreKeys() timestamp := bundle.GetTimestamp() var keys []string @@ -45,7 +44,9 @@ func buildSignatureMaterial(bundle *protobuf.Bundle) []byte { } -func SignBundle(identity *ecdsa.PrivateKey, bundleContainer *protobuf.BundleContainer) error { +// SignBundle signs the bundle and refreshes the timestamps +func SignBundle(identity *ecdsa.PrivateKey, bundleContainer *BundleContainer) error { + bundleContainer.Bundle.Timestamp = time.Now().UnixNano() signatureMaterial := buildSignatureMaterial(bundleContainer.GetBundle()) signature, err := crypto.Sign(crypto.Keccak256(signatureMaterial), identity) @@ -57,7 +58,7 @@ func SignBundle(identity *ecdsa.PrivateKey, bundleContainer *protobuf.BundleCont } // NewBundleContainer creates a new BundleContainer from an identity private key -func NewBundleContainer(identity *ecdsa.PrivateKey, installationID string) (*protobuf.BundleContainer, error) { +func NewBundleContainer(identity *ecdsa.PrivateKey, installationID string) (*BundleContainer, error) { preKey, err := crypto.GenerateKey() if err != nil { return nil, err @@ -67,32 +68,32 @@ func NewBundleContainer(identity *ecdsa.PrivateKey, installationID string) (*pro compressedIdentityKey := crypto.CompressPubkey(&identity.PublicKey) encodedPreKey := crypto.FromECDSA(preKey) - signedPreKeys := make(map[string]*protobuf.SignedPreKey) - signedPreKeys[installationID] = &protobuf.SignedPreKey{ - ProtocolVersion: ProtocolVersion, + signedPreKeys := make(map[string]*SignedPreKey) + signedPreKeys[installationID] = &SignedPreKey{ + ProtocolVersion: protocolVersion, SignedPreKey: compressedPreKey, } - bundle := protobuf.Bundle{ + bundle := Bundle{ Timestamp: time.Now().UnixNano(), Identity: compressedIdentityKey, SignedPreKeys: signedPreKeys, } - return &protobuf.BundleContainer{ + return &BundleContainer{ Bundle: &bundle, PrivateSignedPreKey: encodedPreKey, }, nil } // VerifyBundle checks that a bundle is valid -func VerifyBundle(bundle *protobuf.Bundle) error { +func VerifyBundle(bundle *Bundle) error { _, err := ExtractIdentity(bundle) return err } // ExtractIdentity extracts the identity key from a given bundle -func ExtractIdentity(bundle *protobuf.Bundle) (*ecdsa.PublicKey, error) { +func ExtractIdentity(bundle *Bundle) (*ecdsa.PublicKey, error) { bundleIdentityKey, err := crypto.DecompressPubkey(bundle.GetIdentity()) if err != nil { return nil, err diff --git a/vendor/github.com/status-im/status-protocol-go/go.mod b/vendor/github.com/status-im/status-protocol-go/go.mod new file mode 100644 index 000000000..48707dd05 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/go.mod @@ -0,0 +1,28 @@ +module github.com/status-im/status-protocol-go + +go 1.12 + +require ( + github.com/aristanetworks/goarista v0.0.0-20190704150520-f44d68189fd7 // indirect + github.com/deckarep/golang-set v1.7.1 // indirect + github.com/ethereum/go-ethereum v1.8.27 + github.com/golang/protobuf v1.3.1 + github.com/leodido/go-urn v1.1.0 // indirect + github.com/mutecomm/go-sqlcipher v0.0.0-20170920224653-f799951b4ab2 + github.com/pkg/errors v0.8.1 + github.com/rs/cors v1.6.0 // indirect + github.com/russolsen/transit v0.0.0-20180705123435-0794b4c4505a + github.com/status-im/doubleratchet v2.0.0+incompatible + github.com/status-im/migrate/v4 v4.3.1-status + github.com/status-im/status-go v0.29.0-beta.3 + github.com/status-im/whisper v1.4.14 + github.com/stretchr/testify v1.3.0 + go.uber.org/atomic v1.4.0 // indirect + go.uber.org/multierr v1.1.0 // indirect + go.uber.org/zap v1.10.0 + golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 + golang.org/x/net v0.0.0-20190628185345-da137c7871d7 // indirect + gopkg.in/go-playground/validator.v9 v9.29.0 // indirect +) + +replace github.com/ethereum/go-ethereum v1.8.27 => github.com/status-im/go-ethereum v1.8.27-status.4 diff --git a/vendor/github.com/status-im/status-protocol-go/go.sum b/vendor/github.com/status-im/status-protocol-go/go.sum new file mode 100644 index 000000000..2e2c386a1 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/go.sum @@ -0,0 +1,520 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= +github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/Azure/azure-pipeline-go v0.0.0-20180607212504-7571e8eb0876/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg= +github.com/Azure/azure-storage-blob-go v0.0.0-20180712005634-eaae161d9d5e/go.mod h1:x2mtS6O3mnMEZOJp7d7oldh8IvatBrMfReiyQ+cKgKY= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= +github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/NaySoftware/go-fcm v0.0.0-20190516140123-808e978ddcd2/go.mod h1:3qVrdgWvoMZMoRG+/nusrCNrcP4RYU4MWGv467XjqLI= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/allegro/bigcache v0.0.0-20190218064605-e24eb225f156 h1:hh7BAWFHv41r0gce0KRYtDJpL4erKfmB1/mpgoSADeI= +github.com/allegro/bigcache v0.0.0-20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/aristanetworks/goarista v0.0.0-20190502180301-283422fc1708/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/aristanetworks/goarista v0.0.0-20190704150520-f44d68189fd7 h1:fKnuvQ/O22ZpD7HaJjGQXn/GxOdDJOQFL8bpM8Xe3X8= +github.com/aristanetworks/goarista v0.0.0-20190704150520-f44d68189fd7/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= +github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= +github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c h1:aEbSeNALREWXk0G7UdNhR3ayBV7tZ4M2PNmnrCAph6Q= +github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +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/snappy-go v0.0.0-20151229074030-0bdef8d06723/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/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= +github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg= +github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc= +github.com/cznic/internal v0.0.0-20180608152220-f44710a21d00/go.mod h1:olo7eAdKwJdXxb55TKGLiJ6xt1H0/tiiRCWKVLmtjY4= +github.com/cznic/lldb v1.1.0/go.mod h1:FIZVUmYUVhPwRiPzL8nD/mpFcJ/G7SSXjjXYG4uRI3A= +github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= +github.com/cznic/ql v1.2.0/go.mod h1:FbpzhyZrqr0PVlK6ury+PoW3T0ODUV22OeWIxcaOrSE= +github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ= +github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= +github.com/cznic/zappy v0.0.0-20160723133515-2533cb5b45cc/go.mod h1:Y1SNZ4dRUOKXshKUbwUapqNncRrho4mkjQebgEHZLj8= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= +github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= +github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= +github.com/dgrijalva/jwt-go v0.0.0-20170201225849-2268707a8f08/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dhui/dktest v0.3.0 h1:kwX5a7EkLcjo7VpsPQSYJcKGbXBXdjI9FGjuUj1jn6I= +github.com/dhui/dktest v0.3.0/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc= +github.com/docker/distribution v2.7.0+incompatible h1:neUDAlf3wX6Ml4HdqTrbcOHXtfRN0TFIwt6YFL7N9RU= +github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v0.0.0-20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v0.7.3-0.20190103212154-2b7e084dc98b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v0.7.3-0.20190108045446-77df18c24acf h1:2v/98rHzs3v6X0AHtoCH9u+e56SdnpogB1Z2fFe1KqQ= +github.com/docker/docker v0.7.3-0.20190108045446-77df18c24acf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/elastic/gosigar v0.0.0-20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= +github.com/ethereum/go-ethereum v1.8.20/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= +github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk= +github.com/gizak/termui v0.0.0-20170117222342-991cd3d38091/go.mod h1:PkJoWUt/zacQKysNfQtcw1RW+eK2SxkieVBtl+4ovLA= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= +github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= +github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= +github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.5.4/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA= +github.com/golang-migrate/migrate v3.5.4+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk= +github.com/golang-migrate/migrate/v4 v4.4.0 h1:bQVSVUJCZkAf6ci1J4aUNFO0FyXVoncZvHEFCVD2ezE= +github.com/golang-migrate/migrate/v4 v4.4.0/go.mod h1:SzAcz2l+yDJVhQC7fwiF7T2MAFPMIkigJz98klRJ4OE= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v0.0.0-20170726212829-748d386b5c1e/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +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= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU= +github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= +github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= +github.com/gyuho/goraph v0.0.0-20171001060514-a7a4454fd3eb/go.mod h1:NtSxZCD+s3sZFwbW6WceOcUD83HM9XD5OE2r4c0P8eg= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +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/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag= +github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= +github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= +github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/influxdata/influxdb v0.0.0-20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= +github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= +github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= +github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8= +github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= +github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= +github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= +github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= +github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= +github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= +github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= +github.com/jackpal/go-nat-pmp v0.0.0-20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jackpal/go-nat-pmp v1.0.1 h1:i0LektDkO1QlrTm/cSuP+PyBCDnYvjPLGl4LdWEMiaA= +github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs= +github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= +github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs= +github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= +github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v0.0.0-20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/karalabe/hid v0.0.0-20181128192157-d815e0c1a2e2/go.mod h1:YvbcH+3Wo6XPs9nkgTY3u19KXLauXW+J5nB7hEHuX0A= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kshvakov/clickhouse v1.3.5/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE= +github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= +github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= +github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= +github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= +github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= +github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= +github.com/libp2p/go-libp2p v0.1.1/go.mod h1:I00BRo1UuUSdpuc8Q2mN7yDF/oTUTRAX6JWpTiK9Rp8= +github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= +github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= +github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= +github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= +github.com/libp2p/go-libp2p-core v0.0.3/go.mod h1:j+YQMNz9WNSkNezXOsahp9kwZBKBvxLpKD316QWSJXE= +github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= +github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g= +github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= +github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo= +github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE= +github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY= +github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= +github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= +github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY= +github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8= +github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= +github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= +github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= +github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI= +github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= +github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= +github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= +github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= +github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI= +github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= +github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs= +github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= +github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= +github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc= +github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo= +github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e/go.mod h1:nty42YY5QByNC5MM7q/nj938VbgPU7avs45z6NClpxI= +github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-isatty v0.0.0-20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/mongodb/mongo-go-driver v0.3.0/go.mod h1:NK/HWDIIZkaYsnYa0hmtP443T5ELr0KDecmIioVuuyU= +github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= +github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= +github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= +github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= +github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= +github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= +github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= +github.com/mutecomm/go-sqlcipher v0.0.0-20170920224653-f799951b4ab2 h1:Q5L04ENac5Rx1K+vuxM7cjsrCgqSJNumYy3SHOQC4ec= +github.com/mutecomm/go-sqlcipher v0.0.0-20170920224653-f799951b4ab2/go.mod h1:S9Hougr2LhvUtZGY1fCTj9nr9P53lfsCCQ2XqVMFZX8= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= +github.com/naoina/toml v0.0.0-20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= +github.com/nsf/termbox-go v0.0.0-20170211012700-3540b76b9c77/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= +github.com/olekukonko/tablewriter v0.0.0-20170128050532-febf2d34b54a/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +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/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opentracing/opentracing-go v0.0.0-20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222 h1:goeTyGkArOZIVOMA0dQbyuPWGNQJZGPwPu/QS9GlpnA= +github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= +github.com/peterh/liner v0.0.0-20170902204657-a37ad3984311/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.0.0-20171216070316-e881fd58d78e/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/prometheus v0.0.0-20170814170113-3101606756c5/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= +github.com/robertkrimen/otto v0.0.0-20170205013659-6a77b7cbc37d/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY= +github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= +github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= +github.com/russolsen/ohyeah v0.0.0-20160324131710-f4938c005315 h1:H3hCXwP92pH/hSgNrCLtjxvsKJ50sq26nICbZuoR1tQ= +github.com/russolsen/ohyeah v0.0.0-20160324131710-f4938c005315/go.mod h1:ZbKa3zlLnhGF1dAeJtMSoNtM5LgFQnqzq8eYH3uYYkU= +github.com/russolsen/same v0.0.0-20160222130632-f089df61f51d h1:A926QrjwToaPS7giC4UOBjHhdukq9l1Y15r3qkXYwCY= +github.com/russolsen/same v0.0.0-20160222130632-f089df61f51d/go.mod h1:Cpq811GTlHevuU6BZxk3ObOdK8AY5gHu9QGmDak0DT4= +github.com/russolsen/transit v0.0.0-20180705123435-0794b4c4505a h1:yVNJFSzkEG8smsvd9udiQcMJA0MIsFvlG7ba314cu+s= +github.com/russolsen/transit v0.0.0-20180705123435-0794b4c4505a/go.mod h1:TPq+fcJOdGrkpZpXF4UVmFjYxH0gGqnxdgZ+OzAmvJk= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/status-im/doubleratchet v2.0.0+incompatible h1:s77lF1lDubK0RKftxN2vH8G9gwtVVp13ggWfyY4O1q4= +github.com/status-im/doubleratchet v2.0.0+incompatible/go.mod h1:1sqR0+yhiM/bd+wrdX79AOt2csZuJOni0nUDzKNuqOU= +github.com/status-im/go-ethereum v1.8.27-status.4 h1:8jTQsYDLtKd/XCa++ZkexTPHfANIDh084JbO2SBAwp4= +github.com/status-im/go-ethereum v1.8.27-status.4/go.mod h1:Ulij8LMpMvXnbnPcmDqrpI+iXoXSjxItuY/wmbasTZU= +github.com/status-im/go-multiaddr-ethv4 v1.2.0/go.mod h1:2VQ3C+9zEurcceasz12gPAtmEzCeyLUGPeKLSXYQKHo= +github.com/status-im/migrate/v4 v4.3.1-status h1:tJwsEYLgbFkvlTSMk89APwRDfpr4yG8wcwPeudsqPwo= +github.com/status-im/migrate/v4 v4.3.1-status/go.mod h1:r8HggRBZ/k7TRwByq/Hp3P/ubFppIna0nvyavVK0pjA= +github.com/status-im/rendezvous v1.3.0/go.mod h1:+hzjuP+j/XzLPeF6E50b88pWOTLdTcwjvNYt+Gh1W1s= +github.com/status-im/status-go v0.29.0-beta.3 h1:0MzV9p1r+CVxCBrg+OrGDQ8v/6p+k6Cd3ITQ4XCjFdw= +github.com/status-im/status-go v0.29.0-beta.3/go.mod h1:8OHekmRoYTn9oZgMsBdhMEgeyfaQ4eI4ycOFHJCoX7M= +github.com/status-im/whisper v1.4.14 h1:9VHqx4+PUYfhDnYYtDxHkg/3cfVvkHjPNciY4LO83yc= +github.com/status-im/whisper v1.4.14/go.mod h1:WS6z39YJQ8WJa9s+DmTuEM/s2nVF6Iz3B1SZYw5cYf0= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v0.0.0-20170809224252-890a5c3458b4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/syndtr/goleveldb v0.0.0-20181128100959-b001fa50d6b2/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= +github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/uber/jaeger-client-go v0.0.0-20180607151842-f7e0d4744fa6/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v0.0.0-20180615202729-a51202d6f4a7/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= +github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= +github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8= +github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= +github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= +github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= +github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/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= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/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-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190214214411-e77772198cdc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190426135247-a129542de9ae h1:mQLHiymj/JXKnnjc62tb7nD5pZLs940/sXJu+Xp3DBA= +golang.org/x/sys v0.0.0-20190426135247-a129542de9ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +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= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20170215214335-be0fcc31ae23/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a h1:jd4PGQGmrzmDZANUzIol3eClsCB/Jp5GmpGWMhi6hnY= +golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb h1:i1Ppqkc3WQXikh8bXiwHqAN5Rv3/qDCcRk0/Otx73BY= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v9 v9.9.3/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/go-playground/validator.v9 v9.29.0 h1:5ofssLNYgAA/inWn6rTZ4juWpRJUwEnXc1LG2IeXwgQ= +gopkg.in/go-playground/validator.v9 v9.29.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +gopkg.in/olebedev/go-duktape.v3 v3.0.0-20180302121509-abf0ba0be5d5/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= +gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +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= diff --git a/vendor/github.com/status-im/status-protocol-go/internal/sqlite/migrations.go b/vendor/github.com/status-im/status-protocol-go/internal/sqlite/migrations.go new file mode 100644 index 000000000..31ed65b44 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/internal/sqlite/migrations.go @@ -0,0 +1,367 @@ +// Code generated by go-bindata. DO NOT EDIT. +// sources: +// 000001_add_messages_contacts.down.db.sql (91B) +// 000001_add_messages_contacts.up.db.sql (685B) +// 000002_add_unread_to_user_messages.down.sql (210B) +// 000002_add_unread_to_user_messages.up.sql (113B) +// doc.go (377B) + +package sqlite + +import ( + "bytes" + "compress/gzip" + "crypto/sha256" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo + digest [sha256.Size]byte +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var __000001_add_messages_contactsDownDbSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x28\x2d\x4e\x2d\x8a\xcf\x4d\x2d\x2e\x4e\x4c\x4f\x2d\xb6\xe6\x42\x97\x49\xce\xcf\x2b\x49\x4c\x2e\x41\x95\xc9\xc8\x2c\x2e\xc9\x2f\xaa\x8c\x47\x56\x11\x5f\x92\x5f\x90\x99\x6c\xcd\x05\x08\x00\x00\xff\xff\x1b\x57\x14\x62\x5b\x00\x00\x00") + +func _000001_add_messages_contactsDownDbSqlBytes() ([]byte, error) { + return bindataRead( + __000001_add_messages_contactsDownDbSql, + "000001_add_messages_contacts.down.db.sql", + ) +} + +func _000001_add_messages_contactsDownDbSql() (*asset, error) { + bytes, err := _000001_add_messages_contactsDownDbSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "000001_add_messages_contacts.down.db.sql", size: 91, mode: os.FileMode(0644), modTime: time.Unix(1562955202, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xee, 0x89, 0x87, 0xa3, 0x2, 0x4f, 0xb4, 0x8c, 0x2c, 0x15, 0x41, 0x85, 0x3c, 0xbe, 0x71, 0x55, 0xa1, 0x6a, 0xa1, 0x5e, 0x8e, 0xd8, 0xcf, 0x84, 0xed, 0xb0, 0x60, 0x3, 0xcb, 0xb5, 0x33, 0x7c}} + return a, nil +} + +var __000001_add_messages_contactsUpDbSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x92\xcf\x6e\xb3\x30\x10\xc4\xef\x7e\x8a\x3d\xf2\x49\x1c\xbe\x7b\x4f\x06\x96\xc4\x2a\xb5\x5b\xc7\x34\xc9\x09\x51\xc7\x6a\x50\xc2\x1f\xc5\x8e\x54\xde\xbe\x82\x42\xe3\xa6\x69\xae\x33\xf6\x7a\xe6\xb7\x8e\x25\x52\x85\xa0\x68\x94\x21\xb0\x14\xb8\x50\x80\x1b\xb6\x52\x2b\x38\x5b\x73\x2a\x6a\x63\x6d\xf9\x6e\x2c\x04\xa4\xda\x41\x94\x89\x08\x72\xce\x5e\x72\x1c\x4f\xf2\x3c\xcb\x42\xa2\xdb\xc6\x95\xda\x15\xd5\x0e\x5e\xa9\x8c\x97\x54\x5e\x99\xa6\x71\x85\xeb\x3b\x33\xdb\x21\x99\xc6\x5e\xa9\xce\x7c\x38\x50\xb8\x51\x21\xd1\xc7\x56\x1f\x20\x62\x0b\xc6\x55\x48\x5c\x55\x1b\xeb\xca\xba\xfb\x56\xe6\xb1\x7a\x5f\x8e\x0f\x4f\xb7\xe6\xc7\x2e\x83\xba\xf3\xdb\xb1\xd2\xc5\xc1\xf4\x63\x7a\xf2\xef\x81\x90\xa9\x34\xe3\x09\x6e\xe0\x92\xde\x82\xe0\x3f\x5b\x07\x17\xd3\xbb\xf7\x27\xac\xe9\xf4\x04\x6b\x66\xf1\x2c\xd9\x13\x95\x5b\x78\xc4\xad\xc7\xa5\x29\x6b\x73\x03\x97\x6b\xbb\x4a\x8f\xd1\x7d\x71\xa0\xc4\xb8\x2f\x59\x57\xba\x51\xbb\xd1\x10\xd6\x4c\x2d\x45\xae\x40\x8a\x35\x4b\xee\xe7\xde\x57\xd6\xb5\xa7\xbe\xf0\xf3\x17\x5f\x21\x02\x62\xfb\x46\x9b\xdd\xc4\x1c\x12\x4c\x69\x9e\x29\xf8\x7f\x7f\xf5\xbf\xbe\x47\x2a\x24\xb2\x05\x1f\xfa\xfb\x3c\x41\x62\x8a\x12\x79\x8c\x57\xf4\x82\xc1\x14\x1c\x12\xcc\x50\x21\xc4\x74\x15\xd3\x04\x87\xc5\x7d\x06\x00\x00\xff\xff\x28\x6d\x42\x61\xad\x02\x00\x00") + +func _000001_add_messages_contactsUpDbSqlBytes() ([]byte, error) { + return bindataRead( + __000001_add_messages_contactsUpDbSql, + "000001_add_messages_contacts.up.db.sql", + ) +} + +func _000001_add_messages_contactsUpDbSql() (*asset, error) { + bytes, err := _000001_add_messages_contactsUpDbSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "000001_add_messages_contacts.up.db.sql", size: 685, mode: os.FileMode(0644), modTime: time.Unix(1562955202, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1d, 0xd, 0x9, 0xd8, 0x7d, 0xba, 0x90, 0x3c, 0x7c, 0x6, 0xf2, 0x12, 0xb2, 0xbc, 0xd8, 0xbd, 0xc0, 0xea, 0xde, 0xa7, 0xae, 0x6a, 0x4b, 0xe5, 0x3d, 0xc3, 0xa4, 0x4a, 0x6d, 0x62, 0x2b, 0x91}} + return a, nil +} + +var __000002_add_unread_to_user_messagesDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x44\x8e\x31\x4e\xc5\x30\x10\x44\x7b\x4e\x31\x25\x14\xfe\x07\xa0\xa6\xa1\x4a\x03\x07\xd8\xd8\x43\x62\xc9\xde\xb5\xec\xb5\x42\x6e\x8f\x2c\x21\x68\xdf\xee\xbc\x99\x10\xf0\xb1\xbd\x6d\xcf\x92\xa4\xbe\xbc\x22\x8a\xaa\x39\x52\xb7\x86\x68\x65\x56\xc5\x95\xfd\xb4\xe9\x88\xd6\xee\xac\x07\x04\x2e\x7b\xe1\xe3\x29\x04\xbc\xd7\x56\x58\xa9\xff\xd7\x3c\x40\x19\x37\xf6\xe9\xb8\x08\x25\x13\xdc\x90\x66\x2b\x39\x8a\x13\x23\x9e\xac\xb2\xc2\x6e\xe8\x8c\x9d\x8b\x66\x7f\xe0\x53\x0b\xc7\x80\x9f\xec\x5c\x1e\xc1\xa8\xd2\x9d\x1d\x97\xdc\xeb\xfb\xa0\x2f\xf8\x27\xb0\x2f\x88\x82\xdf\x79\xf8\xaa\xfe\x9d\xf5\x13\x00\x00\xff\xff\x1c\xd9\x39\x28\xd2\x00\x00\x00") + +func _000002_add_unread_to_user_messagesDownSqlBytes() ([]byte, error) { + return bindataRead( + __000002_add_unread_to_user_messagesDownSql, + "000002_add_unread_to_user_messages.down.sql", + ) +} + +func _000002_add_unread_to_user_messagesDownSql() (*asset, error) { + bytes, err := _000002_add_unread_to_user_messagesDownSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "000002_add_unread_to_user_messages.down.sql", size: 210, mode: os.FileMode(0644), modTime: time.Unix(1562955202, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xdc, 0x47, 0x8e, 0x4b, 0x54, 0x6a, 0xfc, 0x61, 0xa5, 0x37, 0xc6, 0xd6, 0xd3, 0x22, 0xcd, 0xbe, 0xb3, 0x60, 0xd8, 0xa0, 0xe2, 0x67, 0x27, 0x5, 0xf3, 0x6d, 0xca, 0x84, 0xd5, 0xfc, 0x94, 0x1a}} + return a, nil +} + +var __000002_add_unread_to_user_messagesUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\xcc\x41\xca\xc2\x30\x10\x40\xe1\x7d\x4f\xf1\x2e\x50\xfe\x7f\xef\x2a\x9a\x08\xc2\x98\x82\x4c\xd6\x12\x70\x2c\xc5\xaa\x90\x21\x9e\x5f\x84\xae\xde\xea\x7d\x41\x34\x5d\xd0\xb0\x97\x44\x77\x6b\xd7\xa7\xb9\xd7\xd9\x7c\x00\x08\x31\x72\x98\xa4\x9c\x33\xf7\xb5\xce\xce\x29\x2b\x79\x52\x72\x11\x21\xa6\x63\x28\xa2\xfc\xef\x18\x47\x3e\xb5\x2d\xef\xee\x6c\xc0\x36\xac\xcb\xc3\x68\x56\x6f\x7f\xfd\xf5\xcb\xf0\x0d\x00\x00\xff\xff\xbc\x55\xda\x47\x71\x00\x00\x00") + +func _000002_add_unread_to_user_messagesUpSqlBytes() ([]byte, error) { + return bindataRead( + __000002_add_unread_to_user_messagesUpSql, + "000002_add_unread_to_user_messages.up.sql", + ) +} + +func _000002_add_unread_to_user_messagesUpSql() (*asset, error) { + bytes, err := _000002_add_unread_to_user_messagesUpSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "000002_add_unread_to_user_messages.up.sql", size: 113, mode: os.FileMode(0644), modTime: time.Unix(1562955202, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x8a, 0x92, 0x67, 0x90, 0x5, 0xb4, 0x82, 0x13, 0x78, 0x83, 0xae, 0x5e, 0x13, 0x67, 0x2f, 0xdd, 0xe1, 0xcb, 0x11, 0x70, 0xdb, 0x5, 0xb6, 0xb0, 0x1c, 0x29, 0x99, 0xae, 0x1a, 0x32, 0xb0, 0x41}} + return a, nil +} + +var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\x3b\x72\xc3\x30\x0c\x44\x7b\x9d\x62\xc7\x8d\x9b\x88\x6c\x52\xa5\x4b\x99\x3e\x17\x80\x49\x88\xc4\x98\x1f\x85\x80\xfc\xb9\x7d\x46\x4e\x66\xe2\x2e\xed\x0e\xde\xe2\xad\xf7\xf8\xcc\xa2\x58\xa4\x30\x44\xd1\x38\xb0\x2a\x8d\x3b\x4e\x1c\x68\x53\xc6\x21\x89\xe5\xed\xe4\x42\xaf\x5e\x8d\x6c\xd3\x59\xaa\xaf\x92\x06\x19\xfb\xcb\xeb\x61\xf2\x1e\x81\xda\xd1\x90\xa9\xc5\xc2\x8f\x2e\x85\x1a\x0d\x93\x96\x70\x15\xcb\x20\xac\x83\x17\xb9\x39\xbc\x1b\x0a\x93\x1a\x2c\x93\x1d\x15\x96\x19\x81\x94\xf7\x9a\xa5\x0f\xa4\x3e\x9f\xa4\x45\x32\x72\x7b\xf4\xb1\x3c\x25\xbb\x61\xa0\x52\x38\x62\x19\xbd\x3e\x58\xa5\xca\x88\x32\x38\x58\x1f\xf7\x17\x90\x2a\x1b\x1a\x55\xd6\x9d\xcf\x74\x61\xb4\xfe\xfb\x1e\xd4\xe2\xff\x8b\x70\xed\xe3\xac\x20\x05\xdf\x56\x0e\xc6\xd1\x4d\xd3\x4a\xe1\x4c\x89\xf1\x73\x27\xbd\xe9\x34\x79\x9f\xfa\x5b\xe2\xc6\x3b\xf9\xec\x39\xaf\xe7\x04\xfd\x2a\x62\x8c\xb9\xc3\x39\xff\x87\xb9\xd4\xe1\xa6\xef\x00\x00\x00\xff\xff\xcd\x86\x58\x5c\x79\x01\x00\x00") + +func docGoBytes() ([]byte, error) { + return bindataRead( + _docGo, + "doc.go", + ) +} + +func docGo() (*asset, error) { + bytes, err := docGoBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "doc.go", size: 377, mode: os.FileMode(0644), modTime: time.Unix(1562955202, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x94, 0xd1, 0x5c, 0x73, 0x47, 0x65, 0xf9, 0x6e, 0xa0, 0xee, 0xb, 0x3d, 0xbe, 0xff, 0xef, 0xae, 0xc9, 0x46, 0x21, 0x85, 0x12, 0x46, 0xa1, 0x73, 0x74, 0xca, 0x71, 0xb1, 0xe1, 0x69, 0xe1, 0x82}} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// AssetString returns the asset contents as a string (instead of a []byte). +func AssetString(name string) (string, error) { + data, err := Asset(name) + return string(data), err +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// MustAssetString is like AssetString but panics when Asset would return an +// error. It simplifies safe initialization of global variables. +func MustAssetString(name string) string { + return string(MustAsset(name)) +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetDigest returns the digest of the file with the given name. It returns an +// error if the asset could not be found or the digest could not be loaded. +func AssetDigest(name string) ([sha256.Size]byte, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err) + } + return a.digest, nil + } + return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name) +} + +// Digests returns a map of all known files and their checksums. +func Digests() (map[string][sha256.Size]byte, error) { + mp := make(map[string][sha256.Size]byte, len(_bindata)) + for name := range _bindata { + a, err := _bindata[name]() + if err != nil { + return nil, err + } + mp[name] = a.digest + } + return mp, nil +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "000001_add_messages_contacts.down.db.sql": _000001_add_messages_contactsDownDbSql, + + "000001_add_messages_contacts.up.db.sql": _000001_add_messages_contactsUpDbSql, + + "000002_add_unread_to_user_messages.down.sql": _000002_add_unread_to_user_messagesDownSql, + + "000002_add_unread_to_user_messages.up.sql": _000002_add_unread_to_user_messagesUpSql, + + "doc.go": docGo, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"}, +// AssetDir("data/img") would return []string{"a.png", "b.png"}, +// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + canonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(canonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "000001_add_messages_contacts.down.db.sql": &bintree{_000001_add_messages_contactsDownDbSql, map[string]*bintree{}}, + "000001_add_messages_contacts.up.db.sql": &bintree{_000001_add_messages_contactsUpDbSql, map[string]*bintree{}}, + "000002_add_unread_to_user_messages.down.sql": &bintree{_000002_add_unread_to_user_messagesDownSql, map[string]*bintree{}}, + "000002_add_unread_to_user_messages.up.sql": &bintree{_000002_add_unread_to_user_messagesUpSql, map[string]*bintree{}}, + "doc.go": &bintree{docGo, map[string]*bintree{}}, +}} + +// RestoreAsset restores an asset under the given directory. +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) +} + +// RestoreAssets restores an asset under the given directory recursively. +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + canonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) +} diff --git a/vendor/github.com/status-im/status-protocol-go/log_encoder.go b/vendor/github.com/status-im/status-protocol-go/log_encoder.go new file mode 100644 index 000000000..fce9b4d31 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/log_encoder.go @@ -0,0 +1,36 @@ +package statusproto + +import ( + "encoding/hex" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +type jsonHexEncoder struct { + zapcore.Encoder +} + +// NewJSONHexEncoder creates a JSON logger based on zapcore.NewJSONEncoder +// but overwrites encoding of byte slices. Instead encoding them with base64, +// jsonHexEncoder uses hex-encoding. +// Each hex-encoded value is prefixed with 0x so that it's clear it's a hex string. +func NewJSONHexEncoder(cfg zapcore.EncoderConfig) zapcore.Encoder { + jsonEncoder := zapcore.NewJSONEncoder(cfg) + return &jsonHexEncoder{ + Encoder: jsonEncoder, + } +} + +func (enc *jsonHexEncoder) AddBinary(key string, val []byte) { + enc.AddString(key, "0x"+hex.EncodeToString(val)) +} + +// RegisterJSONHexEncoder registers a jsonHexEncoder under "json-hex" name. +// Later, this name can be used as a value for zap.Config.Encoding to enable +// jsonHexEncoder. +func RegisterJSONHexEncoder() error { + return zap.RegisterEncoder("json-hex", func(cfg zapcore.EncoderConfig) (zapcore.Encoder, error) { + return NewJSONHexEncoder(cfg), nil + }) +} diff --git a/vendor/github.com/status-im/status-protocol-go/messenger.go b/vendor/github.com/status-im/status-protocol-go/messenger.go new file mode 100644 index 000000000..4b41b51d8 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/messenger.go @@ -0,0 +1,497 @@ +package statusproto + +import ( + "context" + "crypto/ecdsa" + "path/filepath" + "time" + + "go.uber.org/zap" + + "github.com/pkg/errors" + whisper "github.com/status-im/whisper/whisperv6" + + "github.com/status-im/status-protocol-go/encryption" + "github.com/status-im/status-protocol-go/encryption/multidevice" + "github.com/status-im/status-protocol-go/encryption/sharedsecret" + migrations "github.com/status-im/status-protocol-go/internal/sqlite" + "github.com/status-im/status-protocol-go/sqlite" + transport "github.com/status-im/status-protocol-go/transport/whisper" + "github.com/status-im/status-protocol-go/transport/whisper/filter" + protocol "github.com/status-im/status-protocol-go/v1" +) + +const ( + // messagesDatabaseFileName is a name of the SQL file in which + // messages are stored. + messagesDatabaseFileName = "messages.sql" +) + +var ( + ErrChatIDEmpty = errors.New("chat ID is empty") + ErrNotImplemented = errors.New("not implemented") +) + +// Messenger is a entity managing chats and messages. +// It acts as a bridge between the application and encryption +// layers. +// It needs to expose an interface to manage installations +// because installations are managed by the user. +// Similarly, it needs to expose an interface to manage +// mailservers because they can also be managed by the user. +type Messenger struct { + identity *ecdsa.PrivateKey + persistence persistence + adapter *whisperAdapter + encryptor *encryption.Protocol + + ownMessages map[string][]*protocol.Message + featureFlags featureFlags + messagesPersistenceEnabled bool + + logger *zap.Logger + + shutdownTasks []func() error +} + +type featureFlags struct { + genericDiscoveryTopicEnabled bool + // sendV1Messages indicates whether we should send + // messages compatible only with V1 and later. + // V1 messages adds additional wrapping + // which contains a signature and payload. + sendV1Messages bool +} + +type config struct { + onNewInstallationsHandler func([]*multidevice.Installation) + // DEPRECATED: no need to expose it + onNewSharedSecretHandler func([]*sharedsecret.Secret) + // DEPRECATED: no need to expose it + onSendContactCodeHandler func(*encryption.ProtocolMessageSpec) + + encryptionLayerFilePath string + transportLayerFilePath string + + messagesPersistenceEnabled bool + featureFlags featureFlags + + logger *zap.Logger +} + +type Option func(*config) error + +func WithOnNewInstallationsHandler(h func([]*multidevice.Installation)) func(c *config) error { + return func(c *config) error { + c.onNewInstallationsHandler = h + return nil + } +} + +func WithOnNewSharedSecret(h func([]*sharedsecret.Secret)) func(c *config) error { + return func(c *config) error { + c.onNewSharedSecretHandler = h + return nil + } +} + +func WithCustomLogger(logger *zap.Logger) func(c *config) error { + return func(c *config) error { + c.logger = logger + return nil + } +} + +func WithGenericDiscoveryTopicSupport() func(c *config) error { + return func(c *config) error { + c.featureFlags.genericDiscoveryTopicEnabled = true + return nil + } +} + +func WithMessagesPersistenceEnabled() func(c *config) error { + return func(c *config) error { + c.messagesPersistenceEnabled = true + return nil + } +} + +// TODO: use this config fileds. +func WithDatabaseFilePaths(encryptionLayerFilePath, transportLayerFilePath string) func(c *config) error { + return func(c *config) error { + c.encryptionLayerFilePath = encryptionLayerFilePath + c.transportLayerFilePath = transportLayerFilePath + return nil + } +} + +func WithSendV1Messages() func(c *config) error { + return func(c *config) error { + c.featureFlags.sendV1Messages = true + return nil + } +} + +func NewMessenger( + identity *ecdsa.PrivateKey, + server transport.Server, + shh *whisper.Whisper, + dataDir string, + dbKey string, + installationID string, + opts ...Option, +) (*Messenger, error) { + var messenger *Messenger + + c := config{} + + for _, opt := range opts { + if err := opt(&c); err != nil { + return nil, err + } + } + + logger := c.logger + if c.logger == nil { + var err error + if logger, err = zap.NewDevelopment(); err != nil { + return nil, errors.Wrap(err, "failed to create a logger") + } + } + + // Set default config fields. + if c.onNewInstallationsHandler == nil { + c.onNewInstallationsHandler = func(installations []*multidevice.Installation) { + sugar := logger.Sugar().With("site", "onNewInstallationsHandler") + for _, installation := range installations { + sugar.Infow( + "received a new installation", + "identity", installation.Identity, + "id", installation.ID) + } + } + } + if c.onNewSharedSecretHandler == nil { + c.onNewSharedSecretHandler = func(secrets []*sharedsecret.Secret) { + if err := messenger.handleSharedSecrets(secrets); err != nil { + slogger := logger.With(zap.String("site", "onNewSharedSecretHandler")) + slogger.Warn("failed to process secrets", zap.Error(err)) + } + } + } + if c.onSendContactCodeHandler == nil { + c.onSendContactCodeHandler = func(messageSpec *encryption.ProtocolMessageSpec) { + slogger := logger.With(zap.String("site", "onSendContactCodeHandler")) + slogger.Info("received a SendContactCode request") + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + _, err := messenger.adapter.SendContactCode(ctx, messageSpec) + slogger.Warn("failed to send a contact code", zap.Error(err)) + } + } + + // Set default database file paths. + if c.encryptionLayerFilePath == "" { + c.encryptionLayerFilePath = filepath.Join(dataDir, "sessions.sql") + } + if c.transportLayerFilePath == "" { + c.transportLayerFilePath = filepath.Join(dataDir, "transport.sql") + } + + t, err := transport.NewWhisperServiceTransport( + server, + shh, + identity, + c.transportLayerFilePath, + dbKey, + nil, + logger, + ) + if err != nil { + return nil, errors.Wrap(err, "failed to create a WhisperServiceTransport") + } + + encryptionProtocol, err := encryption.New( + c.encryptionLayerFilePath, + dbKey, + installationID, + c.onNewInstallationsHandler, + c.onNewSharedSecretHandler, + c.onSendContactCodeHandler, + logger, + ) + if err != nil { + return nil, errors.Wrap(err, "failed to create the encryption layer") + } + + applicationLayerFilePath := filepath.Join(dataDir, messagesDatabaseFileName) + applicationLayerPersistence, err := sqlite.Open(applicationLayerFilePath, dbKey, sqlite.MigrationConfig{ + AssetNames: migrations.AssetNames(), + AssetGetter: func(name string) ([]byte, error) { + return migrations.Asset(name) + }, + }) + if err != nil { + return nil, errors.Wrap(err, "failed to initialize messages db") + } + + persistence := &sqlitePersistence{db: applicationLayerPersistence} + adapter := newWhisperAdapter(identity, t, encryptionProtocol, c.featureFlags, logger) + messenger = &Messenger{ + identity: identity, + persistence: persistence, + adapter: adapter, + encryptor: encryptionProtocol, + ownMessages: make(map[string][]*protocol.Message), + featureFlags: c.featureFlags, + messagesPersistenceEnabled: c.messagesPersistenceEnabled, + shutdownTasks: []func() error{ + persistence.Close, + adapter.transport.Reset, + // Currently this often fails, seems like it's safe to ignore them + // https://github.com/uber-go/zap/issues/328 + func() error { _ = logger.Sync; return nil }, + }, + logger: logger, + } + + // Start all services immediately. + // TODO: consider removing identity as an argument to Start(). + if err := encryptionProtocol.Start(identity); err != nil { + return nil, err + } + + logger.Debug("messages persistence", zap.Bool("enabled", c.messagesPersistenceEnabled)) + + return messenger, nil +} + +// Shutdown takes care of ensuring a clean shutdown of Messenger +func (m *Messenger) Shutdown() (err error) { + for _, task := range m.shutdownTasks { + if tErr := task(); tErr != nil { + if err == nil { + // First error appeared. + err = tErr + } else { + // We return all errors. They will be concatenated in the order of occurrence, + // however, they will also be returned as a single error. + err = errors.Wrap(err, tErr.Error()) + } + } + } + return +} + +func (m *Messenger) handleSharedSecrets(secrets []*sharedsecret.Secret) error { + return m.adapter.handleSharedSecrets(secrets) +} + +func (m *Messenger) EnableInstallation(id string) error { + return m.encryptor.EnableInstallation(&m.identity.PublicKey, id) +} + +func (m *Messenger) DisableInstallation(id string) error { + return m.encryptor.DisableInstallation(&m.identity.PublicKey, id) +} + +func (m *Messenger) Installations() ([]*multidevice.Installation, error) { + return m.encryptor.GetOurInstallations(&m.identity.PublicKey) +} + +func (m *Messenger) SetInstallationMetadata(id string, data *multidevice.InstallationMetadata) error { + return m.encryptor.SetInstallationMetadata(&m.identity.PublicKey, id, data) +} + +// NOT IMPLEMENTED +func (m *Messenger) SelectMailserver(id string) error { + return ErrNotImplemented +} + +// NOT IMPLEMENTED +func (m *Messenger) AddMailserver(enode string) error { + return ErrNotImplemented +} + +// NOT IMPLEMENTED +func (m *Messenger) RemoveMailserver(id string) error { + return ErrNotImplemented +} + +// NOT IMPLEMENTED +func (m *Messenger) Mailservers() ([]string, error) { + return nil, ErrNotImplemented +} + +func (m *Messenger) Join(chat Chat) error { + if chat.PublicKey() != nil { + return m.adapter.JoinPrivate(chat.PublicKey()) + } else if chat.PublicName() != "" { + return m.adapter.JoinPublic(chat.PublicName()) + } + return errors.New("chat is neither public nor private") +} + +func (m *Messenger) Leave(chat Chat) error { + if chat.PublicKey() != nil { + return m.adapter.LeavePrivate(chat.PublicKey()) + } else if chat.PublicName() != "" { + return m.adapter.LeavePublic(chat.PublicName()) + } + return errors.New("chat is neither public nor private") +} + +func (m *Messenger) Send(ctx context.Context, chat Chat, data []byte) ([]byte, error) { + chatID := chat.ID() + if chatID == "" { + return nil, ErrChatIDEmpty + } + + clock, err := m.persistence.LastMessageClock(chat.ID()) + if err != nil { + return nil, err + } + + if chat.PublicKey() != nil { + hash, message, err := m.adapter.SendPrivate(ctx, chat.PublicKey(), chat.ID(), data, clock) + if err != nil { + return nil, err + } + + // Save our message because it won't be received from the transport layer. + message.ID = hash // a Message need ID to be properly stored in the db + message.SigPubKey = &m.identity.PublicKey + + if m.messagesPersistenceEnabled { + _, err = m.persistence.SaveMessages(chat.ID(), []*protocol.Message{message}) + if err != nil { + return nil, err + } + } + + // Cache it to be returned in Retrieve(). + m.ownMessages[chatID] = append(m.ownMessages[chatID], message) + + return hash, nil + } else if chat.PublicName() != "" { + return m.adapter.SendPublic(ctx, chat.PublicName(), chat.ID(), data, clock) + } + return nil, errors.New("chat is neither public nor private") +} + +// SendRaw takes encoded data, encrypts it and sends through the wire. +// DEPRECATED +func (m *Messenger) SendRaw(ctx context.Context, chat Chat, data []byte) ([]byte, whisper.NewMessage, error) { + if chat.PublicKey() != nil { + return m.adapter.SendPrivateRaw(ctx, chat.PublicKey(), data) + } else if chat.PublicName() != "" { + return m.adapter.SendPublicRaw(ctx, chat.PublicName(), data) + } + return nil, whisper.NewMessage{}, errors.New("chat is neither public nor private") +} + +type RetrieveConfig struct { + From time.Time + To time.Time + latest bool + last24Hours bool +} + +var ( + RetrieveLatest = RetrieveConfig{latest: true} + RetrieveLastDay = RetrieveConfig{latest: true, last24Hours: true} +) + +func (m *Messenger) Retrieve(ctx context.Context, chat Chat, c RetrieveConfig) (messages []*protocol.Message, err error) { + var ( + latest []*protocol.Message + ownLatest []*protocol.Message + ) + + if chat.PublicKey() != nil { + latest, err = m.adapter.RetrievePrivateMessages(chat.PublicKey()) + // Return any own messages for this chat as well. + if ownMessages, ok := m.ownMessages[chat.ID()]; ok { + ownLatest = ownMessages + } + } else if chat.PublicName() != "" { + latest, err = m.adapter.RetrievePublicMessages(chat.PublicName()) + } else { + return nil, errors.New("chat is neither public nor private") + } + + if err != nil { + err = errors.Wrap(err, "failed to retrieve messages") + return + } + + if m.messagesPersistenceEnabled { + _, err = m.persistence.SaveMessages(chat.ID(), latest) + if err != nil { + return nil, errors.Wrap(err, "failed to save latest messages") + } + } + + // Confirm received and decrypted messages. + if m.messagesPersistenceEnabled && chat.PublicKey() != nil { + for _, message := range latest { + // Confirm received and decrypted messages. + if err := m.encryptor.ConfirmMessageProcessed(message.ID); err != nil { + return nil, errors.Wrap(err, "failed to confirm message being processed") + } + } + } + + // When our messages are returned, we can delete them. + delete(m.ownMessages, chat.ID()) + + return m.retrieveSaved(ctx, chat, c, append(latest, ownLatest...)) +} + +func (m *Messenger) retrieveSaved(ctx context.Context, chat Chat, c RetrieveConfig, latest []*protocol.Message) (messages []*protocol.Message, err error) { + if !m.messagesPersistenceEnabled { + return latest, nil + } + + if !c.latest { + return m.persistence.Messages(chat.ID(), c.From, c.To) + } + + if c.last24Hours { + to := time.Now() + from := to.Add(-time.Hour * 24) + return m.persistence.Messages(chat.ID(), from, to) + } + + return latest, nil +} + +// DEPRECATED +func (m *Messenger) RetrieveRawAll() (map[filter.Chat][]*whisper.Message, error) { + return m.adapter.RetrieveRawAll() +} + +// DEPRECATED +func (m *Messenger) RetrieveRawWithFilter(filterID string) ([]*whisper.Message, error) { + return m.adapter.RetrieveRaw(filterID) +} + +// DEPRECATED +func (m *Messenger) LoadFilters(chats []*filter.Chat) ([]*filter.Chat, error) { + return m.adapter.transport.LoadFilters(chats, m.featureFlags.genericDiscoveryTopicEnabled) +} + +// DEPRECATED +func (m *Messenger) RemoveFilters(chats []*filter.Chat) error { + return m.adapter.transport.RemoveFilters(chats) +} + +// DEPRECATED +func (m *Messenger) ConfirmMessagesProcessed(messageIDs [][]byte) error { + for _, id := range messageIDs { + if err := m.encryptor.ConfirmMessageProcessed(id); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/status-im/status-protocol-go/persistence.go b/vendor/github.com/status-im/status-protocol-go/persistence.go new file mode 100644 index 000000000..62e50ce7b --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/persistence.go @@ -0,0 +1,258 @@ +package statusproto + +import ( + "context" + "crypto/ecdsa" + "database/sql" + "time" + + "github.com/ethereum/go-ethereum/crypto/secp256k1" + "github.com/pkg/errors" + + protocol "github.com/status-im/status-protocol-go/v1" +) + +const ( + uniqueIDContstraint = "UNIQUE constraint failed: user_messages.id" +) + +var ( + // ErrMsgAlreadyExist returned if msg already exist. + ErrMsgAlreadyExist = errors.New("message with given ID already exist") +) + +// persistence is an interface for all db operations. +type persistence interface { + Close() error + Messages(chatID string, from, to time.Time) ([]*protocol.Message, error) + NewMessages(chatID string, rowid int64) ([]*protocol.Message, error) + UnreadMessages(chatID string) ([]*protocol.Message, error) + SaveMessages(chatID string, messages []*protocol.Message) (int64, error) + LastMessageClock(chatID string) (int64, error) +} + +// sqlitePersistence wrapper around sql db with operations common for a client. +type sqlitePersistence struct { + db *sql.DB +} + +// Close closes internal sqlite database. +func (db sqlitePersistence) Close() error { + return db.db.Close() +} + +func (db sqlitePersistence) LastMessageClock(chatID string) (int64, error) { + if chatID == "" { + return 0, errors.New("chat ID is empty") + } + + var last sql.NullInt64 + err := db.db.QueryRow("SELECT max(clock) FROM user_messages WHERE contact_id = ?", chatID).Scan(&last) + if err != nil { + return 0, err + } + return last.Int64, nil +} + +func (db sqlitePersistence) SaveMessages(chatID string, messages []*protocol.Message) (last int64, err error) { + var ( + tx *sql.Tx + stmt *sql.Stmt + ) + tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{}) + if err != nil { + return + } + stmt, err = tx.Prepare(`INSERT INTO user_messages( +id, contact_id, content_type, message_type, text, clock, timestamp, content_chat_id, content_text, public_key, flags) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`) + if err != nil { + return + } + defer func() { + if err == nil { + err = tx.Commit() + return + + } + // don't shadow original error + _ = tx.Rollback() + }() + + var rst sql.Result + + for _, msg := range messages { + pkey := []byte{} + if msg.SigPubKey != nil { + pkey, err = marshalECDSAPub(msg.SigPubKey) + } + rst, err = stmt.Exec( + msg.ID, chatID, msg.ContentT, msg.MessageT, msg.Text, + msg.Clock, msg.Timestamp, msg.Content.ChatID, msg.Content.Text, + pkey, msg.Flags) + if err != nil { + if err.Error() == uniqueIDContstraint { + // skip duplicated messages + err = nil + continue + } + return + } + + last, err = rst.LastInsertId() + if err != nil { + return + } + } + return +} + +// Messages returns messages for a given contact, in a given period. Ordered by a timestamp. +func (db sqlitePersistence) Messages(chatID string, from, to time.Time) (result []*protocol.Message, err error) { + rows, err := db.db.Query(`SELECT +id, content_type, message_type, text, clock, timestamp, content_chat_id, content_text, public_key, flags +FROM user_messages WHERE contact_id = ? AND timestamp >= ? AND timestamp <= ? ORDER BY timestamp`, + chatID, protocol.TimestampInMsFromTime(from), protocol.TimestampInMsFromTime(to)) + if err != nil { + return nil, err + } + var ( + rst = []*protocol.Message{} + ) + for rows.Next() { + msg := protocol.Message{ + Content: protocol.Content{}, + } + pkey := []byte{} + err = rows.Scan( + &msg.ID, &msg.ContentT, &msg.MessageT, &msg.Text, &msg.Clock, + &msg.Timestamp, &msg.Content.ChatID, &msg.Content.Text, &pkey, &msg.Flags) + if err != nil { + return nil, err + } + if len(pkey) != 0 { + msg.SigPubKey, err = unmarshalECDSAPub(pkey) + if err != nil { + return nil, err + } + } + rst = append(rst, &msg) + } + return rst, nil +} + +func (db sqlitePersistence) NewMessages(chatID string, rowid int64) ([]*protocol.Message, error) { + rows, err := db.db.Query(`SELECT +id, content_type, message_type, text, clock, timestamp, content_chat_id, content_text, public_key, flags +FROM user_messages WHERE contact_id = ? AND rowid >= ? ORDER BY clock`, + chatID, rowid) + if err != nil { + return nil, err + } + var ( + rst = []*protocol.Message{} + ) + for rows.Next() { + msg := protocol.Message{ + Content: protocol.Content{}, + } + pkey := []byte{} + err = rows.Scan( + &msg.ID, &msg.ContentT, &msg.MessageT, &msg.Text, &msg.Clock, + &msg.Timestamp, &msg.Content.ChatID, &msg.Content.Text, &pkey, &msg.Flags) + if err != nil { + return nil, err + } + if len(pkey) != 0 { + msg.SigPubKey, err = unmarshalECDSAPub(pkey) + if err != nil { + return nil, err + } + } + rst = append(rst, &msg) + } + return rst, nil +} + +// TODO(adam): refactor all message getters in order not to +// repeat the select fields over and over. +func (db sqlitePersistence) UnreadMessages(chatID string) ([]*protocol.Message, error) { + rows, err := db.db.Query(` + SELECT + id, + content_type, + message_type, + text, + clock, + timestamp, + content_chat_id, + content_text, + public_key, + flags + FROM + user_messages + WHERE + contact_id = ? AND + flags & ? == 0 + ORDER BY clock`, + chatID, protocol.MessageRead, + ) + if err != nil { + return nil, err + } + + var result []*protocol.Message + + for rows.Next() { + msg := protocol.Message{ + Content: protocol.Content{}, + } + pkey := []byte{} + err = rows.Scan( + &msg.ID, &msg.ContentT, &msg.MessageT, &msg.Text, &msg.Clock, + &msg.Timestamp, &msg.Content.ChatID, &msg.Content.Text, &pkey, &msg.Flags) + if err != nil { + return nil, err + } + if len(pkey) != 0 { + msg.SigPubKey, err = unmarshalECDSAPub(pkey) + if err != nil { + return nil, err + } + } + result = append(result, &msg) + } + + return result, nil +} + +func marshalECDSAPub(pub *ecdsa.PublicKey) (rst []byte, err error) { + switch pub.Curve.(type) { + case *secp256k1.BitCurve: + rst = make([]byte, 34) + rst[0] = 1 + copy(rst[1:], secp256k1.CompressPubkey(pub.X, pub.Y)) + return rst[:], nil + default: + return nil, errors.New("unknown curve") + } +} + +func unmarshalECDSAPub(buf []byte) (*ecdsa.PublicKey, error) { + pub := &ecdsa.PublicKey{} + if len(buf) < 1 { + return nil, errors.New("too small") + } + switch buf[0] { + case 1: + pub.Curve = secp256k1.S256() + pub.X, pub.Y = secp256k1.DecompressPubkey(buf[1:]) + ok := pub.IsOnCurve(pub.X, pub.Y) + if !ok { + return nil, errors.New("not on curve") + } + return pub, nil + default: + return nil, errors.New("unknown curve") + } +} diff --git a/vendor/github.com/status-im/status-protocol-go/sqlite/db.go b/vendor/github.com/status-im/status-protocol-go/sqlite/db.go new file mode 100644 index 000000000..00e607574 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/sqlite/db.go @@ -0,0 +1,120 @@ +package sqlite + +import ( + "database/sql" + "fmt" + "os" + + _ "github.com/mutecomm/go-sqlcipher" // We require go sqlcipher that overrides default implementation + "github.com/status-im/migrate/v4" + "github.com/status-im/migrate/v4/database/sqlcipher" + bindata "github.com/status-im/migrate/v4/source/go_bindata" +) + +// The default number of kdf iterations in sqlcipher (from version 3.0.0) +// https://github.com/sqlcipher/sqlcipher/blob/fda4c68bb474da7e955be07a2b807bda1bb19bd2/CHANGELOG.md#300---2013-11-05 +// https://www.zetetic.net/sqlcipher/sqlcipher-api/#kdf_iter +const defaultKdfIterationsNumber = 64000 // nolint: deadcode,varcheck,unused + +// The reduced number of kdf iterations (for performance reasons) which is +// currently used for derivation of the database key +// https://github.com/status-im/status-go/pull/1343 +// https://notes.status.im/i8Y_l7ccTiOYq09HVgoFwA +const reducedKdfIterationsNumber = 3200 + +// MigrationConfig is a struct that allows to define bindata migrations. +type MigrationConfig struct { + AssetNames []string + AssetGetter func(name string) ([]byte, error) +} + +// Open opens or initializes a new database for a given file path. +// MigrationConfig is optional but if provided migrations are applied automatically. +func Open(path, key string, mc MigrationConfig) (*sql.DB, error) { + return open(path, key, reducedKdfIterationsNumber, mc) +} + +// OpenWithIter allows to open a new database with a custom number of kdf iterations. +// Higher kdf iterations number makes it slower to open the database. +func OpenWithIter(path, key string, kdfIter int, mc MigrationConfig) (*sql.DB, error) { + return open(path, key, kdfIter, mc) +} + +func open(path string, key string, kdfIter int, mc MigrationConfig) (*sql.DB, error) { + _, err := os.OpenFile(path, os.O_CREATE, 0644) + if err != nil { + return nil, err + } + + db, err := sql.Open("sqlite3", path) + if err != nil { + return nil, err + } + + keyString := fmt.Sprintf("PRAGMA key = '%s'", key) + + // Disable concurrent access as not supported by the driver + db.SetMaxOpenConns(1) + + if _, err = db.Exec("PRAGMA foreign_keys=ON"); err != nil { + return nil, err + } + + if _, err = db.Exec(keyString); err != nil { + return nil, err + } + + kdfString := fmt.Sprintf("PRAGMA kdf_iter = '%d'", kdfIter) + + if _, err = db.Exec(kdfString); err != nil { + return nil, err + } + + // Migrate db + if len(mc.AssetNames) == 0 { + if err := db.Ping(); err != nil { + return nil, err + } + return db, nil + } + + if err = ApplyMigrations(db, mc.AssetNames, mc.AssetGetter); err != nil { + return nil, err + } + return db, nil +} + +// ApplyMigrations allows to apply bindata migrations on the current *sql.DB. +// `assetNames` is a list of assets with migrations and `assetGetter` is responsible +// for returning the content of the asset with a given name. +func ApplyMigrations(db *sql.DB, assetNames []string, assetGetter func(name string) ([]byte, error)) error { + resources := bindata.Resource( + assetNames, + assetGetter, + ) + + source, err := bindata.WithInstance(resources) + if err != nil { + return err + } + + driver, err := sqlcipher.WithInstance(db, &sqlcipher.Config{}) + if err != nil { + return err + } + + m, err := migrate.NewWithInstance( + "go-bindata", + source, + "sqlcipher", + driver) + if err != nil { + return err + } + + if err = m.Up(); err != migrate.ErrNoChange { + return err + } + + return nil +} diff --git a/vendor/github.com/status-im/status-protocol-go/sqlite/doc.go b/vendor/github.com/status-im/status-protocol-go/sqlite/doc.go new file mode 100644 index 000000000..dd971beda --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/sqlite/doc.go @@ -0,0 +1,7 @@ +// Package sqlite is responsible for creation of encrypted sqlite3 database using sqlcipher driver. +// It is optimized for mobile usage as well. +// +// sqlite package also provides a capability to apply bindata migration. +// You can keep your migrations close to your business logic and use this package +// to create an encrypted sqlite3 database and then apply the migrations easily. +package sqlite diff --git a/vendor/github.com/status-im/status-protocol-go/transport/whisper/filter/chats.go b/vendor/github.com/status-im/status-protocol-go/transport/whisper/filter/chats.go new file mode 100644 index 000000000..eb26832a0 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/transport/whisper/filter/chats.go @@ -0,0 +1,587 @@ +package filter + +import ( + "crypto/ecdsa" + "database/sql" + "encoding/hex" + "math/big" + "strconv" + "sync" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" + whisper "github.com/status-im/whisper/whisperv6" + "go.uber.org/zap" +) + +const ( + DiscoveryTopic = "contact-discovery" +) + +var ( + // The number of partitions. + nPartitions = big.NewInt(5000) + minPow = 0.0 +) + +type Filter struct { + FilterID string + Topic whisper.TopicType + SymKeyID string +} + +type NegotiatedSecret struct { + PublicKey *ecdsa.PublicKey + Key []byte +} + +// TODO: revise fields encoding/decoding. Some are encoded using hexutil and some using encoding/hex. +type Chat struct { + // ChatID is the identifier of the chat + ChatID string `json:"chatId"` + // FilterID the whisper filter id generated + FilterID string `json:"filterId"` + // SymKeyID is the symmetric key id used for symmetric chats + SymKeyID string `json:"symKeyId"` + // OneToOne tells us if we need to use asymmetric encryption for this chat + OneToOne bool `json:"oneToOne"` + // Identity is the public key of the other recipient for non-public chats. + // It's encoded using encoding/hex. + Identity string `json:"identity"` + // Topic is the whisper topic + Topic whisper.TopicType `json:"topic"` + // Discovery is whether this is a discovery topic + Discovery bool `json:"discovery"` + // Negotiated tells us whether is a negotiated topic + Negotiated bool `json:"negotiated"` + // Listen is whether we are actually listening for messages on this chat, or the filter is only created in order to be able to post on the topic + Listen bool `json:"listen"` +} + +type ChatsManager struct { + whisper *whisper.Whisper + persistence *sqlitePersistence + privateKey *ecdsa.PrivateKey + keys map[string][]byte // a cache of symmetric keys derived from passwords + logger *zap.Logger + + genericDiscoveryTopicEnabled bool + + mutex sync.Mutex + chats map[string]*Chat +} + +// New returns a new ChatsManager service +func New(db *sql.DB, w *whisper.Whisper, privateKey *ecdsa.PrivateKey, logger *zap.Logger) (*ChatsManager, error) { + if logger == nil { + logger = zap.NewNop() + } + + persistence, err := newSQLitePersistence(db) + if err != nil { + return nil, err + } + + keys, err := persistence.All() + if err != nil { + return nil, err + } + + return &ChatsManager{ + privateKey: privateKey, + whisper: w, + persistence: persistence, + keys: keys, + chats: make(map[string]*Chat), + logger: logger.With(zap.Namespace("ChatsManager")), + }, nil +} + +func (s *ChatsManager) Init( + chatIDs []string, + publicKeys []*ecdsa.PublicKey, + genericDiscoveryTopicEnabled bool, +) ([]*Chat, error) { + logger := s.logger.With(zap.String("site", "Init")) + + logger.Info("initializing") + + s.genericDiscoveryTopicEnabled = genericDiscoveryTopicEnabled + + // Load our contact code. + _, err := s.LoadContactCode(&s.privateKey.PublicKey) + if err != nil { + return nil, errors.Wrap(err, "failed to load contact code") + } + + // Load partitioned topic. + _, err = s.loadMyPartitioned() + if err != nil { + return nil, err + } + + // Add discovery topic. + _, err = s.LoadDiscovery() + if err != nil { + return nil, err + } + + // Add public, one-to-one and negotiated chats. + for _, chatID := range chatIDs { + _, err := s.LoadPublic(chatID) + if err != nil { + return nil, err + } + } + + for _, publicKey := range publicKeys { + _, err := s.LoadContactCode(publicKey) + if err != nil { + return nil, err + } + } + + s.mutex.Lock() + defer s.mutex.Unlock() + + var allChats []*Chat + for _, chat := range s.chats { + allChats = append(allChats, chat) + } + return allChats, nil +} + +// DEPRECATED +func (s *ChatsManager) InitWithChats(chats []*Chat, genericDiscoveryTopicEnabled bool) ([]*Chat, error) { + var ( + chatIDs []string + publicKeys []*ecdsa.PublicKey + ) + + for _, chat := range chats { + if chat.Identity != "" && chat.OneToOne { + publicKey, err := strToPublicKey(chat.Identity) + if err != nil { + return nil, err + } + publicKeys = append(publicKeys, publicKey) + } else if chat.ChatID != "" { + chatIDs = append(chatIDs, chat.ChatID) + } + } + + return s.Init(chatIDs, publicKeys, genericDiscoveryTopicEnabled) +} + +func (s *ChatsManager) Reset() error { + var chats []*Chat + + s.mutex.Lock() + for _, chat := range s.chats { + chats = append(chats, chat) + } + s.mutex.Unlock() + + return s.Remove(chats...) +} + +func (s *ChatsManager) Chats() (result []*Chat) { + s.mutex.Lock() + defer s.mutex.Unlock() + + for _, chat := range s.chats { + result = append(result, chat) + } + + return +} + +// ChatByID returns a chat by id. +func (s *ChatsManager) ChatByID(chatID string) *Chat { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.chats[chatID] +} + +func (s *ChatsManager) ChatByFilterID(filterID string) *Chat { + s.mutex.Lock() + defer s.mutex.Unlock() + for _, chat := range s.chats { + if chat.FilterID == filterID { + return chat + } + } + return nil +} + +func (s *ChatsManager) ChatsByPublicKey(publicKey *ecdsa.PublicKey) (result []*Chat) { + s.mutex.Lock() + defer s.mutex.Unlock() + + identityStr := publicKeyToStr(publicKey) + + for _, chat := range s.chats { + if chat.Identity == identityStr { + result = append(result, chat) + } + } + + return +} + +// Remove remove all the filters associated with a chat/identity +func (s *ChatsManager) Remove(chats ...*Chat) error { + s.mutex.Lock() + defer s.mutex.Unlock() + + for _, chat := range chats { + if err := s.whisper.Unsubscribe(chat.FilterID); err != nil { + return err + } + if chat.SymKeyID != "" { + s.whisper.DeleteSymKey(chat.SymKeyID) + } + delete(s.chats, chat.ChatID) + } + + return nil +} + +// LoadPartitioned creates a filter for a partitioned topic. +func (s *ChatsManager) LoadPartitioned(publicKey *ecdsa.PublicKey) (*Chat, error) { + return s.loadPartitioned(publicKey, false) +} + +func (s *ChatsManager) loadMyPartitioned() (*Chat, error) { + return s.loadPartitioned(&s.privateKey.PublicKey, true) +} + +func (s *ChatsManager) loadPartitioned(publicKey *ecdsa.PublicKey, listen bool) (*Chat, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + chatID := partitionedTopic(publicKey) + if _, ok := s.chats[chatID]; ok { + return s.chats[chatID], nil + } + + // We set up a filter so we can publish, + // but we discard envelopes if listen is false. + filter, err := s.addAsymmetric(chatID, listen) + if err != nil { + return nil, err + } + + chat := &Chat{ + ChatID: chatID, + FilterID: filter.FilterID, + Topic: filter.Topic, + Identity: publicKeyToStr(publicKey), + Listen: listen, + } + + s.chats[chatID] = chat + + return chat, nil +} + +// LoadNegotiated loads a negotiated secret as a filter. +func (s *ChatsManager) LoadNegotiated(secret NegotiatedSecret) (*Chat, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + chatID := negotiatedTopic(secret.PublicKey) + + if _, ok := s.chats[chatID]; ok { + return s.chats[chatID], nil + } + + keyString := hex.EncodeToString(secret.Key) + filter, err := s.addSymmetric(keyString) + if err != nil { + return nil, err + } + + chat := &Chat{ + ChatID: chatID, + Topic: filter.Topic, + SymKeyID: filter.SymKeyID, + FilterID: filter.FilterID, + Identity: publicKeyToStr(secret.PublicKey), + Negotiated: true, + Listen: true, + } + + s.chats[chat.ChatID] = chat + + return chat, nil +} + +// LoadDiscovery adds 1-2 discovery filters: one for generic discovery topic (if enabled) +// and one for the personal discovery topic. +func (s *ChatsManager) LoadDiscovery() ([]*Chat, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + personalDiscoveryTopic := personalDiscoveryTopic(&s.privateKey.PublicKey) + + // Check if chats are already loaded. + var result []*Chat + + expectedTopicCount := 1 + + if s.genericDiscoveryTopicEnabled { + expectedTopicCount = 2 + if chat, ok := s.chats[DiscoveryTopic]; ok { + result = append(result, chat) + } + } + if chat, ok := s.chats[personalDiscoveryTopic]; ok { + result = append(result, chat) + } + + if len(result) == expectedTopicCount { + return result, nil + } + + var discoveryResponse *Filter + var err error + identityStr := publicKeyToStr(&s.privateKey.PublicKey) + + // Load personal discovery + personalDiscoveryChat := &Chat{ + ChatID: personalDiscoveryTopic, + Identity: identityStr, + Discovery: true, + Listen: true, + } + + discoveryResponse, err = s.addAsymmetric(personalDiscoveryChat.ChatID, true) + if err != nil { + return nil, err + } + + personalDiscoveryChat.Topic = discoveryResponse.Topic + personalDiscoveryChat.FilterID = discoveryResponse.FilterID + + s.chats[personalDiscoveryChat.ChatID] = personalDiscoveryChat + + if s.genericDiscoveryTopicEnabled { + // Load generic discovery topic. + discoveryChat := &Chat{ + ChatID: DiscoveryTopic, + Identity: identityStr, + Discovery: true, + Listen: true, + } + + discoveryResponse, err = s.addAsymmetric(discoveryChat.ChatID, true) + if err != nil { + return nil, err + } + + discoveryChat.Topic = discoveryResponse.Topic + discoveryChat.FilterID = discoveryResponse.FilterID + + s.chats[discoveryChat.ChatID] = discoveryChat + + return []*Chat{discoveryChat, personalDiscoveryChat}, nil + } + + return []*Chat{personalDiscoveryChat}, nil +} + +// LoadPublic adds a filter for a public chat. +func (s *ChatsManager) LoadPublic(chatID string) (*Chat, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + if chat, ok := s.chats[chatID]; ok { + return chat, nil + } + + filterAndTopic, err := s.addSymmetric(chatID) + if err != nil { + return nil, err + } + + chat := &Chat{ + ChatID: chatID, + FilterID: filterAndTopic.FilterID, + SymKeyID: filterAndTopic.SymKeyID, + Topic: filterAndTopic.Topic, + Listen: true, + } + + s.chats[chatID] = chat + + return chat, nil +} + +// LoadContactCode creates a filter for the advertise topic for a given public key. +func (s *ChatsManager) LoadContactCode(pubKey *ecdsa.PublicKey) (*Chat, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + chatID := contactCodeTopic(pubKey) + + if _, ok := s.chats[chatID]; ok { + return s.chats[chatID], nil + } + + contactCodeFilter, err := s.addSymmetric(chatID) + if err != nil { + return nil, err + } + + chat := &Chat{ + ChatID: chatID, + FilterID: contactCodeFilter.FilterID, + Topic: contactCodeFilter.Topic, + SymKeyID: contactCodeFilter.SymKeyID, + Identity: publicKeyToStr(pubKey), + Listen: true, + } + + s.chats[chatID] = chat + return chat, nil +} + +// addSymmetric adds a symmetric key filter +func (s *ChatsManager) addSymmetric(chatID string) (*Filter, error) { + var symKeyID string + var err error + + topic := toTopic(chatID) + topics := [][]byte{topic} + + symKey, ok := s.keys[chatID] + if ok { + symKeyID, err = s.whisper.AddSymKeyDirect(symKey) + if err != nil { + return nil, err + } + } else { + symKeyID, err = s.whisper.AddSymKeyFromPassword(chatID) + if err != nil { + return nil, err + } + if symKey, err = s.whisper.GetSymKey(symKeyID); err != nil { + return nil, err + } + s.keys[chatID] = symKey + + err = s.persistence.Add(chatID, symKey) + if err != nil { + return nil, err + } + } + + f := &whisper.Filter{ + KeySym: symKey, + PoW: minPow, + AllowP2P: true, + Topics: topics, + Messages: s.whisper.NewMessageStore(), + } + + id, err := s.whisper.Subscribe(f) + if err != nil { + return nil, err + } + + return &Filter{ + FilterID: id, + SymKeyID: symKeyID, + Topic: whisper.BytesToTopic(topic), + }, nil +} + +// addAsymmetricFilter adds a filter with our private key +// and set minPow according to the listen parameter. +func (s *ChatsManager) addAsymmetric(chatID string, listen bool) (*Filter, error) { + var ( + err error + pow = 1.0 // use PoW high enough to discard all messages for the filter + ) + + if listen { + pow = minPow + } + + topic := toTopic(chatID) + topics := [][]byte{topic} + + f := &whisper.Filter{ + KeyAsym: s.privateKey, + PoW: pow, + AllowP2P: true, + Topics: topics, + Messages: s.whisper.NewMessageStore(), + } + + id, err := s.whisper.Subscribe(f) + if err != nil { + return nil, err + } + return &Filter{FilterID: id, Topic: whisper.BytesToTopic(topic)}, nil +} + +// GetNegotiated returns a negotiated chat given an identity +func (s *ChatsManager) GetNegotiated(identity *ecdsa.PublicKey) *Chat { + s.mutex.Lock() + defer s.mutex.Unlock() + + return s.chats[negotiatedTopic(identity)] +} + +// toTopic converts a string to a whisper topic. +func toTopic(s string) []byte { + return crypto.Keccak256([]byte(s))[:whisper.TopicLength] +} + +func ToTopic(s string) []byte { + return toTopic(s) +} + +func strToPublicKey(str string) (*ecdsa.PublicKey, error) { + publicKeyBytes, err := hex.DecodeString(str) + if err != nil { + return nil, err + } + return crypto.UnmarshalPubkey(publicKeyBytes) +} + +func publicKeyToStr(publicKey *ecdsa.PublicKey) string { + return hex.EncodeToString(crypto.FromECDSAPub(publicKey)) +} + +func personalDiscoveryTopic(publicKey *ecdsa.PublicKey) string { + return "contact-discovery-" + publicKeyToStr(publicKey) +} + +// partitionedTopic returns the associated partitioned topic string +// with the given public key. +func partitionedTopic(publicKey *ecdsa.PublicKey) string { + partition := big.NewInt(0) + partition.Mod(publicKey.X, nPartitions) + return "contact-discovery-" + strconv.FormatInt(partition.Int64(), 10) +} + +// PublicKeyToPartitionedTopicBytes returns the bytes of the partitioned topic +// associated with the given public key +func PublicKeyToPartitionedTopicBytes(publicKey *ecdsa.PublicKey) []byte { + return toTopic(partitionedTopic(publicKey)) +} + +func ContactCodeTopic(publicKey *ecdsa.PublicKey) string { + return contactCodeTopic(publicKey) +} + +func contactCodeTopic(publicKey *ecdsa.PublicKey) string { + return "0x" + publicKeyToStr(publicKey) + "-contact-code" +} + +func negotiatedTopic(publicKey *ecdsa.PublicKey) string { + return "0x" + publicKeyToStr(publicKey) + "-negotiated" +} diff --git a/vendor/github.com/status-im/status-protocol-go/transport/whisper/filter/doc.go b/vendor/github.com/status-im/status-protocol-go/transport/whisper/filter/doc.go new file mode 100644 index 000000000..5e712eebf --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/transport/whisper/filter/doc.go @@ -0,0 +1,3 @@ +package filter + +// TODO: describe all types of filters. \ No newline at end of file diff --git a/messaging/filter/persistence.go b/vendor/github.com/status-im/status-protocol-go/transport/whisper/filter/persistence.go similarity index 51% rename from messaging/filter/persistence.go rename to vendor/github.com/status-im/status-protocol-go/transport/whisper/filter/persistence.go index 4ce7e3243..2c9a89541 100644 --- a/messaging/filter/persistence.go +++ b/vendor/github.com/status-im/status-protocol-go/transport/whisper/filter/persistence.go @@ -2,22 +2,41 @@ package filter import ( "database/sql" + + "github.com/status-im/status-protocol-go/sqlite" + migrations "github.com/status-im/status-protocol-go/transport/whisper/internal/sqlite" ) -type Persistence interface { - Add(chatID string, key []byte) error - All() (map[string][]byte, error) -} - -type SQLLitePersistence struct { +type sqlitePersistence struct { db *sql.DB } -func NewSQLLitePersistence(db *sql.DB) *SQLLitePersistence { - return &SQLLitePersistence{db: db} +func newSQLitePersistence(db *sql.DB) (*sqlitePersistence, error) { + err := sqlite.ApplyMigrations( + db, + migrations.AssetNames(), + func(name string) ([]byte, error) { + return migrations.Asset(name) + }) + if err != nil { + return nil, nil + } + return &sqlitePersistence{db: db}, nil } -func (s *SQLLitePersistence) All() (map[string][]byte, error) { +func (s *sqlitePersistence) Add(chatID string, key []byte) error { + statement := "INSERT INTO whisper_keys(chat_id, key) VALUES(?, ?)" + stmt, err := s.db.Prepare(statement) + if err != nil { + return err + } + defer stmt.Close() + + _, err = stmt.Exec(chatID, key) + return err +} + +func (s *sqlitePersistence) All() (map[string][]byte, error) { keys := make(map[string][]byte) statement := "SELECT chat_id, key FROM whisper_keys" @@ -34,8 +53,11 @@ func (s *SQLLitePersistence) All() (map[string][]byte, error) { } for rows.Next() { - var chatID string - var key []byte + var ( + chatID string + key []byte + ) + err := rows.Scan(&chatID, &key) if err != nil { return nil, err @@ -45,15 +67,3 @@ func (s *SQLLitePersistence) All() (map[string][]byte, error) { return keys, nil } - -func (s *SQLLitePersistence) Add(chatID string, key []byte) error { - statement := "INSERT INTO whisper_keys(chat_id, key) VALUES(?, ?)" - stmt, err := s.db.Prepare(statement) - if err != nil { - return err - } - defer stmt.Close() - - _, err = stmt.Exec(chatID, key) - return err -} diff --git a/vendor/github.com/status-im/status-protocol-go/transport/whisper/internal/sqlite/migrations.go b/vendor/github.com/status-im/status-protocol-go/transport/whisper/internal/sqlite/migrations.go new file mode 100644 index 000000000..2346b31dd --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/transport/whisper/internal/sqlite/migrations.go @@ -0,0 +1,319 @@ +// Code generated by go-bindata. DO NOT EDIT. +// sources: +// 1561059285_add_whisper_keys.down.sql (25B) +// 1561059285_add_whisper_keys.up.sql (112B) +// doc.go (377B) + +package sqlite + +import ( + "bytes" + "compress/gzip" + "crypto/sha256" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo + digest [sha256.Size]byte +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var __1561059285_add_whisper_keysDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x28\xcf\xc8\x2c\x2e\x48\x2d\x8a\xcf\x4e\xad\x2c\xb6\xe6\x02\x04\x00\x00\xff\xff\x42\x93\x8e\x79\x19\x00\x00\x00") + +func _1561059285_add_whisper_keysDownSqlBytes() ([]byte, error) { + return bindataRead( + __1561059285_add_whisper_keysDownSql, + "1561059285_add_whisper_keys.down.sql", + ) +} + +func _1561059285_add_whisper_keysDownSql() (*asset, error) { + bytes, err := _1561059285_add_whisper_keysDownSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1561059285_add_whisper_keys.down.sql", size: 25, mode: os.FileMode(0644), modTime: time.Unix(1562956766, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb9, 0x31, 0x3f, 0xce, 0xfa, 0x44, 0x36, 0x1b, 0xb0, 0xec, 0x5d, 0xb, 0x90, 0xb, 0x21, 0x4f, 0xd5, 0xe5, 0x50, 0xed, 0xc7, 0x43, 0xdf, 0x83, 0xb4, 0x3a, 0xc1, 0x55, 0x2e, 0x53, 0x7c, 0x67}} + return a, nil +} + +var __1561059285_add_whisper_keysUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x04\xc0\xb1\x0a\xc2\x40\x0c\x06\xe0\xfd\x9e\xe2\x1f\x15\x7c\x03\xa7\xde\x19\x35\x18\x13\x09\x29\xb5\x53\x11\x3d\x68\xe9\x22\x56\x90\xbe\xbd\x5f\x71\x6a\x82\x10\x4d\x16\xc2\x6f\x9c\x96\x77\xfd\x0c\x73\x5d\x17\x6c\x12\xf0\x1c\x1f\xdf\x61\x7a\x21\xe8\x1e\xb8\x39\x5f\x1b\xef\x71\xa1\x1e\xa6\x28\xa6\x47\xe1\x12\xe0\x93\x9a\xd3\x2e\x01\x73\x5d\x91\xc5\x32\xd4\x02\xda\x8a\xa4\x2d\x3a\x8e\xb3\xb5\x01\xb7\x8e\x0f\xfb\xf4\x0f\x00\x00\xff\xff\x6e\x23\x28\x7d\x70\x00\x00\x00") + +func _1561059285_add_whisper_keysUpSqlBytes() ([]byte, error) { + return bindataRead( + __1561059285_add_whisper_keysUpSql, + "1561059285_add_whisper_keys.up.sql", + ) +} + +func _1561059285_add_whisper_keysUpSql() (*asset, error) { + bytes, err := _1561059285_add_whisper_keysUpSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1561059285_add_whisper_keys.up.sql", size: 112, mode: os.FileMode(0644), modTime: time.Unix(1562956695, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x25, 0x41, 0xc, 0x92, 0xdd, 0x9e, 0xff, 0x5d, 0xd0, 0x93, 0xe4, 0x24, 0x50, 0x29, 0xcf, 0xc6, 0xf7, 0x49, 0x3c, 0x73, 0xd9, 0x8c, 0xfa, 0xf2, 0xcf, 0xf6, 0x6f, 0xbc, 0x31, 0xe6, 0xf7, 0xe2}} + return a, nil +} + +var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\x3b\x72\xc3\x30\x0c\x44\x7b\x9d\x62\xc7\x8d\x9b\x88\x6c\x52\xa5\x4b\x99\x3e\x17\x80\x49\x88\xc4\x98\x1f\x85\x80\xfc\xb9\x7d\x46\x4e\x66\xe2\x2e\xed\x0e\xde\xe2\xad\xf7\xf8\xcc\xa2\x58\xa4\x30\x44\xd1\x38\xb0\x2a\x8d\x3b\x4e\x1c\x68\x53\xc6\x21\x89\xe5\xed\xe4\x42\xaf\x5e\x8d\x6c\xd3\x59\xaa\xaf\x92\x06\x19\xfb\xcb\xeb\x61\xf2\x1e\x81\xda\xd1\x90\xa9\xc5\xc2\x8f\x2e\x85\x1a\x0d\x93\x96\x70\x15\xcb\x20\xac\x83\x17\xb9\x39\xbc\x1b\x0a\x93\x1a\x2c\x93\x1d\x15\x96\x19\x81\x94\xf7\x9a\xa5\x0f\xa4\x3e\x9f\xa4\x45\x32\x72\x7b\xf4\xb1\x3c\x25\xbb\x61\xa0\x52\x38\x62\x19\xbd\x3e\x58\xa5\xca\x88\x32\x38\x58\x1f\xf7\x17\x90\x2a\x1b\x1a\x55\xd6\x9d\xcf\x74\x61\xb4\xfe\xfb\x1e\xd4\xe2\xff\x8b\x70\xed\xe3\xac\x20\x05\xdf\x56\x0e\xc6\xd1\x4d\xd3\x4a\xe1\x4c\x89\xf1\x73\x27\xbd\xe9\x34\x79\x9f\xfa\x5b\xe2\xc6\x3b\xf9\xec\x39\xaf\xe7\x04\xfd\x2a\x62\x8c\xb9\xc3\x39\xff\x87\xb9\xd4\xe1\xa6\xef\x00\x00\x00\xff\xff\xcd\x86\x58\x5c\x79\x01\x00\x00") + +func docGoBytes() ([]byte, error) { + return bindataRead( + _docGo, + "doc.go", + ) +} + +func docGo() (*asset, error) { + bytes, err := docGoBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "doc.go", size: 377, mode: os.FileMode(0644), modTime: time.Unix(1562956695, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x94, 0xd1, 0x5c, 0x73, 0x47, 0x65, 0xf9, 0x6e, 0xa0, 0xee, 0xb, 0x3d, 0xbe, 0xff, 0xef, 0xae, 0xc9, 0x46, 0x21, 0x85, 0x12, 0x46, 0xa1, 0x73, 0x74, 0xca, 0x71, 0xb1, 0xe1, 0x69, 0xe1, 0x82}} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// AssetString returns the asset contents as a string (instead of a []byte). +func AssetString(name string) (string, error) { + data, err := Asset(name) + return string(data), err +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// MustAssetString is like AssetString but panics when Asset would return an +// error. It simplifies safe initialization of global variables. +func MustAssetString(name string) string { + return string(MustAsset(name)) +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetDigest returns the digest of the file with the given name. It returns an +// error if the asset could not be found or the digest could not be loaded. +func AssetDigest(name string) ([sha256.Size]byte, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err) + } + return a.digest, nil + } + return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name) +} + +// Digests returns a map of all known files and their checksums. +func Digests() (map[string][sha256.Size]byte, error) { + mp := make(map[string][sha256.Size]byte, len(_bindata)) + for name := range _bindata { + a, err := _bindata[name]() + if err != nil { + return nil, err + } + mp[name] = a.digest + } + return mp, nil +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "1561059285_add_whisper_keys.down.sql": _1561059285_add_whisper_keysDownSql, + + "1561059285_add_whisper_keys.up.sql": _1561059285_add_whisper_keysUpSql, + + "doc.go": docGo, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"}, +// AssetDir("data/img") would return []string{"a.png", "b.png"}, +// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + canonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(canonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "1561059285_add_whisper_keys.down.sql": &bintree{_1561059285_add_whisper_keysDownSql, map[string]*bintree{}}, + "1561059285_add_whisper_keys.up.sql": &bintree{_1561059285_add_whisper_keysUpSql, map[string]*bintree{}}, + "doc.go": &bintree{docGo, map[string]*bintree{}}, +}} + +// RestoreAsset restores an asset under the given directory. +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) +} + +// RestoreAssets restores an asset under the given directory recursively. +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + canonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) +} diff --git a/vendor/github.com/status-im/status-protocol-go/transport/whisper/server.go b/vendor/github.com/status-im/status-protocol-go/transport/whisper/server.go new file mode 100644 index 000000000..3c3bd99e9 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/transport/whisper/server.go @@ -0,0 +1,59 @@ +package whisper + +import ( + "context" + "crypto/ecdsa" + "time" + + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/pkg/errors" +) + +type Server interface { + Connected(enode.ID) (bool, error) + AddPeer(string) error + NodeID() *ecdsa.PrivateKey +} + +// dialOpts used in Dial function. +type dialOpts struct { + // PollInterval is used for time.Ticker. Must be greated then zero. + PollInterval time.Duration +} + +// dial selected peer and wait until it is connected. +func dial(ctx context.Context, srv Server, peer string, opts dialOpts) error { + if opts.PollInterval == 0 { + return errors.New("poll interval cannot be zero") + } + if err := srv.AddPeer(peer); err != nil { + return err + } + parsed, err := enode.ParseV4(peer) + if err != nil { + return err + } + connected, err := srv.Connected(parsed.ID()) + if err != nil { + return err + } + if connected { + return nil + } + period := time.NewTicker(opts.PollInterval) + defer period.Stop() + for { + select { + case <-ctx.Done(): + return ctx.Err() + case <-period.C: + connected, err := srv.Connected(parsed.ID()) + if err != nil { + return err + } + if connected { + return nil + } + } + } +} diff --git a/vendor/github.com/status-im/status-protocol-go/transport/whisper/utils.go b/vendor/github.com/status-im/status-protocol-go/transport/whisper/utils.go new file mode 100644 index 000000000..aeb1c85bd --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/transport/whisper/utils.go @@ -0,0 +1,10 @@ +package whisper + +import ( + "math/rand" +) + +func randomItem(items []string) string { + l := len(items) + return items[rand.Intn(l)] +} diff --git a/vendor/github.com/status-im/status-protocol-go/transport/whisper/whisper.go b/vendor/github.com/status-im/status-protocol-go/transport/whisper/whisper.go new file mode 100644 index 000000000..012332176 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/transport/whisper/whisper.go @@ -0,0 +1,27 @@ +package whisper + +import ( + whisper "github.com/status-im/whisper/whisperv6" +) + +type RequestOptions struct { + Topics []whisper.TopicType + Password string + Limit int + From int64 // in seconds + To int64 // in seconds +} + +const ( + defaultPowTime = 1 +) + +func DefaultWhisperMessage() whisper.NewMessage { + msg := whisper.NewMessage{} + + msg.TTL = 10 + msg.PowTarget = 0.002 + msg.PowTime = defaultPowTime + + return msg +} diff --git a/vendor/github.com/status-im/status-protocol-go/transport/whisper/whisper_service.go b/vendor/github.com/status-im/status-protocol-go/transport/whisper/whisper_service.go new file mode 100644 index 000000000..d79ec1faa --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/transport/whisper/whisper_service.go @@ -0,0 +1,678 @@ +package whisper + +import ( + "context" + "crypto/ecdsa" + "encoding/hex" + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/rlp" + "github.com/pkg/errors" + "github.com/status-im/status-go/mailserver" + whisper "github.com/status-im/whisper/whisperv6" + "go.uber.org/zap" + + "github.com/status-im/status-protocol-go/sqlite" + "github.com/status-im/status-protocol-go/transport/whisper/filter" + migrations "github.com/status-im/status-protocol-go/transport/whisper/internal/sqlite" +) + +const ( + // defaultRequestTimeout is the default request timeout in seconds + defaultRequestTimeout = 10 +) + +var ( + // ErrNoMailservers returned if there is no configured mailservers that can be used. + ErrNoMailservers = errors.New("no configured mailservers") +) + +type whisperServiceKeysManager struct { + shh *whisper.Whisper + + // Identity of the current user. + privateKey *ecdsa.PrivateKey + + passToSymKeyMutex sync.RWMutex + passToSymKeyCache map[string]string +} + +func (m *whisperServiceKeysManager) AddOrGetKeyPair(priv *ecdsa.PrivateKey) (string, error) { + // caching is handled in Whisper + return m.shh.AddKeyPair(priv) +} + +func (m *whisperServiceKeysManager) AddOrGetSymKeyFromPassword(password string) (string, error) { + m.passToSymKeyMutex.Lock() + defer m.passToSymKeyMutex.Unlock() + + if val, ok := m.passToSymKeyCache[password]; ok { + return val, nil + } + + id, err := m.shh.AddSymKeyFromPassword(password) + if err != nil { + return id, err + } + + m.passToSymKeyCache[password] = id + + return id, nil +} + +func (m *whisperServiceKeysManager) RawSymKey(id string) ([]byte, error) { + return m.shh.GetSymKey(id) +} + +// WhisperServiceTransport is a transport based on Whisper service. +type WhisperServiceTransport struct { + node Server + shh *whisper.Whisper + shhAPI *whisper.PublicWhisperAPI // only PublicWhisperAPI implements logic to send messages + keysManager *whisperServiceKeysManager + chats *filter.ChatsManager + logger *zap.Logger + + mailservers []string + selectedMailServerEnode string +} + +// NewWhisperService returns a new WhisperServiceTransport. +func NewWhisperServiceTransport( + node Server, + shh *whisper.Whisper, + privateKey *ecdsa.PrivateKey, + dbPath string, + dbKey string, + mailservers []string, + logger *zap.Logger, +) (*WhisperServiceTransport, error) { + // DB is shared between this package and all sub-packages. + db, err := sqlite.Open(dbPath, dbKey, sqlite.MigrationConfig{ + AssetNames: migrations.AssetNames(), + AssetGetter: func(name string) ([]byte, error) { + return migrations.Asset(name) + }, + }) + if err != nil { + return nil, err + } + + chats, err := filter.New(db, shh, privateKey, logger) + if err != nil { + return nil, err + } + + return &WhisperServiceTransport{ + node: node, + shh: shh, + shhAPI: whisper.NewPublicWhisperAPI(shh), + keysManager: &whisperServiceKeysManager{ + shh: shh, + privateKey: privateKey, + passToSymKeyCache: make(map[string]string), + }, + chats: chats, + mailservers: mailservers, + logger: logger.With(zap.Namespace("WhisperServiceTransport")), + }, nil +} + +// DEPRECATED +func (a *WhisperServiceTransport) LoadFilters(chats []*filter.Chat, genericDiscoveryTopicEnabled bool) ([]*filter.Chat, error) { + return a.chats.InitWithChats(chats, genericDiscoveryTopicEnabled) +} + +// DEPRECATED +func (a *WhisperServiceTransport) RemoveFilters(chats []*filter.Chat) error { + return a.chats.Remove(chats...) +} + +func (a *WhisperServiceTransport) Reset() error { + return a.chats.Reset() +} + +func (a *WhisperServiceTransport) ProcessNegotiatedSecret(secret filter.NegotiatedSecret) error { + _, err := a.chats.LoadNegotiated(secret) + return err +} + +func (a *WhisperServiceTransport) JoinPublic(chatID string) error { + _, err := a.chats.LoadPublic(chatID) + return err +} + +func (a *WhisperServiceTransport) LeavePublic(chatID string) error { + chat := a.chats.ChatByID(chatID) + if chat != nil { + return nil + } + return a.chats.Remove(chat) +} + +func (a *WhisperServiceTransport) JoinPrivate(publicKey *ecdsa.PublicKey) error { + _, err := a.chats.LoadContactCode(publicKey) + return err +} + +func (a *WhisperServiceTransport) LeavePrivate(publicKey *ecdsa.PublicKey) error { + chats := a.chats.ChatsByPublicKey(publicKey) + return a.chats.Remove(chats...) +} + +func (a *WhisperServiceTransport) RetrievePublicMessages(chatID string) ([]*whisper.ReceivedMessage, error) { + chat, err := a.chats.LoadPublic(chatID) + if err != nil { + return nil, err + } + + f := a.shh.GetFilter(chat.FilterID) + if f == nil { + return nil, errors.New("failed to return a filter") + } + + return f.Retrieve(), nil +} + +func (a *WhisperServiceTransport) RetrievePrivateMessages(publicKey *ecdsa.PublicKey) ([]*whisper.ReceivedMessage, error) { + chats := a.chats.ChatsByPublicKey(publicKey) + discoveryChats, err := a.chats.LoadDiscovery() + if err != nil { + return nil, err + } + + var result []*whisper.ReceivedMessage + + for _, chat := range append(chats, discoveryChats...) { + f := a.shh.GetFilter(chat.FilterID) + if f == nil { + return nil, errors.New("failed to return a filter") + } + result = append(result, f.Retrieve()...) + } + + return result, nil +} + +// DEPRECATED +func (a *WhisperServiceTransport) RetrieveRawAll() (map[filter.Chat][]*whisper.ReceivedMessage, error) { + result := make(map[filter.Chat][]*whisper.ReceivedMessage) + + allChats := a.chats.Chats() + for _, chat := range allChats { + f := a.shh.GetFilter(chat.FilterID) + if f == nil { + return nil, errors.New("failed to return a filter") + } + result[*chat] = append(result[*chat], f.Retrieve()...) + } + + return result, nil +} + +// DEPRECATED +func (a *WhisperServiceTransport) RetrieveRaw(filterID string) ([]*whisper.ReceivedMessage, error) { + f := a.shh.GetFilter(filterID) + if f == nil { + return nil, errors.New("failed to return a filter") + } + return f.Retrieve(), nil +} + +// SendPublic sends a new message using the Whisper service. +// For public chats, chat name is used as an ID as well as +// a topic. +func (a *WhisperServiceTransport) SendPublic(ctx context.Context, newMessage whisper.NewMessage, chatName string) ([]byte, error) { + if err := a.addSig(&newMessage); err != nil { + return nil, err + } + + chat, err := a.chats.LoadPublic(chatName) + if err != nil { + return nil, err + } + + newMessage.SymKeyID = chat.SymKeyID + newMessage.Topic = chat.Topic + + return a.shhAPI.Post(ctx, newMessage) +} + +func (a *WhisperServiceTransport) SendPrivateWithSharedSecret(ctx context.Context, newMessage whisper.NewMessage, publicKey *ecdsa.PublicKey, secret []byte) ([]byte, error) { + if err := a.addSig(&newMessage); err != nil { + return nil, err + } + + chat, err := a.chats.LoadNegotiated(filter.NegotiatedSecret{ + PublicKey: publicKey, + Key: secret, + }) + if err != nil { + return nil, err + } + + newMessage.SymKeyID = chat.SymKeyID + newMessage.Topic = chat.Topic + newMessage.PublicKey = nil + + return a.shhAPI.Post(ctx, newMessage) +} + +func (a *WhisperServiceTransport) SendPrivateWithPartitioned(ctx context.Context, newMessage whisper.NewMessage, publicKey *ecdsa.PublicKey) ([]byte, error) { + if err := a.addSig(&newMessage); err != nil { + return nil, err + } + + chat, err := a.chats.LoadPartitioned(publicKey) + if err != nil { + return nil, err + } + + newMessage.Topic = chat.Topic + newMessage.PublicKey = crypto.FromECDSAPub(publicKey) + + return a.shhAPI.Post(ctx, newMessage) +} + +func (a *WhisperServiceTransport) SendPrivateOnDiscovery(ctx context.Context, newMessage whisper.NewMessage, publicKey *ecdsa.PublicKey) ([]byte, error) { + if err := a.addSig(&newMessage); err != nil { + return nil, err + } + + // There is no need to load any chat + // because listening on the discovery topic + // is done automatically. + // TODO: change this anyway, it should be explicit + // and idempotent. + + newMessage.Topic = whisper.BytesToTopic( + filter.ToTopic(filter.DiscoveryTopic), + ) + newMessage.PublicKey = crypto.FromECDSAPub(publicKey) + + return a.shhAPI.Post(ctx, newMessage) +} + +func (a *WhisperServiceTransport) addSig(newMessage *whisper.NewMessage) error { + sigID, err := a.keysManager.AddOrGetKeyPair(a.keysManager.privateKey) + if err != nil { + return err + } + newMessage.Sig = sigID + return nil +} + +// Request requests messages from mail servers. +func (a *WhisperServiceTransport) Request(ctx context.Context, options RequestOptions) error { + // TODO: remove from here. MailServerEnode must be provided in the params. + enode, err := a.selectAndAddMailServer() + if err != nil { + return err + } + + keyID, err := a.keysManager.AddOrGetSymKeyFromPassword(options.Password) + if err != nil { + return err + } + + req, err := createRequestMessagesParam(enode, keyID, options) + if err != nil { + return err + } + + _, err = a.requestMessages(ctx, req, true) + return err +} + +func (a *WhisperServiceTransport) requestMessages(ctx context.Context, req MessagesRequest, followCursor bool) (resp MessagesResponse, err error) { + logger := a.logger.With(zap.String("site", "requestMessages")) + + logger.Debug("request for a chunk", zap.Uint32("message-limit", req.Limit)) + + start := time.Now() + resp, err = a.requestMessagesWithRetry(RetryConfig{ + BaseTimeout: time.Second * 10, + StepTimeout: time.Second, + MaxRetries: 3, + }, req) + if err != nil { + logger.Error("failed requesting messages", zap.Error(err)) + return + } + + logger.Debug("message delivery summary", + zap.Uint32("message-limit", req.Limit), + zap.Duration("duration", time.Since(start)), + zap.Any("response", resp), + ) + + if resp.Error != nil { + err = resp.Error + return + } + if !followCursor || resp.Cursor == "" { + return + } + + req.Cursor = resp.Cursor + logger.Debug("requesting messages with cursor", zap.String("cursor", req.Cursor)) + return a.requestMessages(ctx, req, true) +} + +// MessagesRequest is a RequestMessages() request payload. +type MessagesRequest struct { + // MailServerPeer is MailServer's enode address. + MailServerPeer string `json:"mailServerPeer"` + + // From is a lower bound of time range (optional). + // Default is 24 hours back from now. + From uint32 `json:"from"` + + // To is a upper bound of time range (optional). + // Default is now. + To uint32 `json:"to"` + + // Limit determines the number of messages sent by the mail server + // for the current paginated request + Limit uint32 `json:"limit"` + + // Cursor is used as starting point for paginated requests + Cursor string `json:"cursor"` + + // Topic is a regular Whisper topic. + // DEPRECATED + Topic whisper.TopicType `json:"topic"` + + // Topics is a list of Whisper topics. + Topics []whisper.TopicType `json:"topics"` + + // SymKeyID is an ID of a symmetric key to authenticate to MailServer. + // It's derived from MailServer password. + SymKeyID string `json:"symKeyID"` + + // Timeout is the time to live of the request specified in seconds. + // Default is 10 seconds + Timeout time.Duration `json:"timeout"` + + // Force ensures that requests will bypass enforced delay. + // TODO(adam): it's currently not handled. + Force bool `json:"force"` +} + +func (r *MessagesRequest) setDefaults(now time.Time) { + // set From and To defaults + if r.To == 0 { + r.To = uint32(now.UTC().Unix()) + } + + if r.From == 0 { + oneDay := uint32(86400) // -24 hours + if r.To < oneDay { + r.From = 0 + } else { + r.From = r.To - oneDay + } + } + + if r.Timeout == 0 { + r.Timeout = defaultRequestTimeout + } +} + +type MessagesResponse struct { + // Cursor from the response can be used to retrieve more messages + // for the previous request. + Cursor string `json:"cursor"` + + // Error indicates that something wrong happened when sending messages + // to the requester. + Error error `json:"error"` +} + +// RetryConfig specifies configuration for retries with timeout and max amount of retries. +type RetryConfig struct { + BaseTimeout time.Duration + // StepTimeout defines duration increase per each retry. + StepTimeout time.Duration + MaxRetries int +} + +func (a *WhisperServiceTransport) requestMessagesWithRetry(conf RetryConfig, r MessagesRequest) (MessagesResponse, error) { + var ( + resp MessagesResponse + requestID hexutil.Bytes + err error + retries int + ) + + logger := a.logger.With(zap.String("site", "requestMessagesWithRetry")) + + events := make(chan whisper.EnvelopeEvent, 10) + + for retries <= conf.MaxRetries { + sub := a.shh.SubscribeEnvelopeEvents(events) + r.Timeout = conf.BaseTimeout + conf.StepTimeout*time.Duration(retries) + timeout := r.Timeout + // FIXME this weird conversion is required because MessagesRequest expects seconds but defines time.Duration + r.Timeout = time.Duration(int(r.Timeout.Seconds())) + requestID, err = a.requestMessagesSync(context.Background(), r) + if err != nil { + sub.Unsubscribe() + return resp, err + } + + mailServerResp, err := waitForExpiredOrCompleted(common.BytesToHash(requestID), events, timeout) + sub.Unsubscribe() + if err == nil { + resp.Cursor = hex.EncodeToString(mailServerResp.Cursor) + resp.Error = mailServerResp.Error + return resp, nil + } + retries++ + logger.Warn("requestMessagesSync failed, retrying", zap.Int("retries", retries), zap.Error(err)) + } + return resp, fmt.Errorf("failed to request messages after %d retries", retries) +} + +// RequestMessages sends a request for historic messages to a MailServer. +func (a *WhisperServiceTransport) requestMessagesSync(_ context.Context, r MessagesRequest) (hexutil.Bytes, error) { + now := a.shh.GetCurrentTime() + r.setDefaults(now) + + if r.From > r.To { + return nil, fmt.Errorf("Query range is invalid: from > to (%d > %d)", r.From, r.To) + } + + // TODO: bring mailserverspackage here + mailServerNode, err := enode.ParseV4(r.MailServerPeer) + if err != nil { + return nil, fmt.Errorf("invalid MailServerPeer: %v", err) + } + + var ( + symKey []byte + publicKey *ecdsa.PublicKey + ) + + if r.SymKeyID != "" { + symKey, err = a.shh.GetSymKey(r.SymKeyID) + if err != nil { + return nil, fmt.Errorf("invalid SymKeyID: %v", err) + } + } else { + publicKey = mailServerNode.Pubkey() + } + + payload, err := makeMessagesRequestPayload(r) + if err != nil { + return nil, err + } + + envelope, err := makeEnvelop( + payload, + symKey, + publicKey, + a.node.NodeID(), + a.shh.MinPow(), + now, + ) + if err != nil { + return nil, err + } + hash := envelope.Hash() + + if err := a.shh.RequestHistoricMessagesWithTimeout(mailServerNode.ID().Bytes(), envelope, r.Timeout*time.Second); err != nil { + return nil, err + } + + return hash[:], nil +} + +func (a *WhisperServiceTransport) selectAndAddMailServer() (string, error) { + logger := a.logger.With(zap.String("site", "selectAndAddMailServer")) + + var enodeAddr string + if a.selectedMailServerEnode != "" { + enodeAddr = a.selectedMailServerEnode + } else { + if len(a.mailservers) == 0 { + return "", ErrNoMailservers + } + enodeAddr = randomItem(a.mailservers) + } + logger.Debug("dialing mail server", zap.String("enode", enodeAddr)) + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + err := dial(ctx, a.node, enodeAddr, dialOpts{PollInterval: 200 * time.Millisecond}) + cancel() + if err == nil { + a.selectedMailServerEnode = enodeAddr + return enodeAddr, nil + } + return "", fmt.Errorf("peer %s failed to connect: %v", enodeAddr, err) +} + +func createRequestMessagesParam(enode, symKeyID string, options RequestOptions) (MessagesRequest, error) { + req := MessagesRequest{ + MailServerPeer: enode, + From: uint32(options.From), // TODO: change to int in status-go + To: uint32(options.To), // TODO: change to int in status-go + Limit: uint32(options.Limit), // TODO: change to int in status-go + SymKeyID: symKeyID, + Topics: options.Topics, + } + + return req, nil +} + +func waitForExpiredOrCompleted(requestID common.Hash, events chan whisper.EnvelopeEvent, timeout time.Duration) (*whisper.MailServerResponse, error) { + expired := fmt.Errorf("request %x expired", requestID) + after := time.NewTimer(timeout) + defer after.Stop() + for { + var ev whisper.EnvelopeEvent + select { + case ev = <-events: + case <-after.C: + return nil, expired + } + if ev.Hash != requestID { + continue + } + switch ev.Event { + case whisper.EventMailServerRequestCompleted: + data, ok := ev.Data.(*whisper.MailServerResponse) + if ok { + return data, nil + } + return nil, errors.New("invalid event data type") + case whisper.EventMailServerRequestExpired: + return nil, expired + } + } +} + +// makeMessagesRequestPayload makes a specific payload for MailServer +// to request historic messages. +func makeMessagesRequestPayload(r MessagesRequest) ([]byte, error) { + cursor, err := hex.DecodeString(r.Cursor) + if err != nil { + return nil, fmt.Errorf("invalid cursor: %v", err) + } + + if len(cursor) > 0 && len(cursor) != mailserver.CursorLength { + return nil, fmt.Errorf("invalid cursor size: expected %d but got %d", mailserver.CursorLength, len(cursor)) + } + + payload := mailserver.MessagesRequestPayload{ + Lower: r.From, + Upper: r.To, + Bloom: createBloomFilter(r), + Limit: r.Limit, + Cursor: cursor, + // Client must tell the MailServer if it supports batch responses. + // This can be removed in the future. + Batch: true, + } + + return rlp.EncodeToBytes(payload) +} + +// makeEnvelop makes an envelop for a historic messages request. +// Symmetric key is used to authenticate to MailServer. +// PK is the current node ID. +func makeEnvelop( + payload []byte, + symKey []byte, + publicKey *ecdsa.PublicKey, + nodeID *ecdsa.PrivateKey, + pow float64, + now time.Time, +) (*whisper.Envelope, error) { + params := whisper.MessageParams{ + PoW: pow, + Payload: payload, + WorkTime: DefaultWhisperMessage().PowTime, + Src: nodeID, + } + // Either symKey or public key is required. + // This condition is verified in `message.Wrap()` method. + if len(symKey) > 0 { + params.KeySym = symKey + } else if publicKey != nil { + params.Dst = publicKey + } + message, err := whisper.NewSentMessage(¶ms) + if err != nil { + return nil, err + } + return message.Wrap(¶ms, now) +} + +func createBloomFilter(r MessagesRequest) []byte { + if len(r.Topics) > 0 { + return topicsToBloom(r.Topics...) + } + + return whisper.TopicToBloom(r.Topic) +} + +func topicsToBloom(topics ...whisper.TopicType) []byte { + i := new(big.Int) + for _, topic := range topics { + bloom := whisper.TopicToBloom(topic) + i.Or(i, new(big.Int).SetBytes(bloom[:])) + } + + combined := make([]byte, whisper.BloomFilterSize) + data := i.Bytes() + copy(combined[whisper.BloomFilterSize-len(data):], data[:]) + + return combined +} diff --git a/vendor/github.com/status-im/status-protocol-go/v1/clock.go b/vendor/github.com/status-im/status-protocol-go/v1/clock.go new file mode 100644 index 000000000..617f262d3 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/v1/clock.go @@ -0,0 +1,20 @@ +package statusproto + +import "time" + +const clockBumpInMs = int64(time.Minute / time.Millisecond) + +// CalcMessageClock calculates a new clock value for Message. +// It is used to properly sort messages and accomodate the fact +// that time might be different on each device. +func CalcMessageClock(lastObservedValue int64, timeInMs TimestampInMs) int64 { + clock := lastObservedValue + if clock < int64(timeInMs) { + // Added time should be larger than time skew tollerance for a message. + // Here, we use 1 minute which is larger than accepted message time skew by Whisper. + clock = int64(timeInMs) + clockBumpInMs + } else { + clock++ + } + return clock +} diff --git a/vendor/github.com/status-im/status-protocol-go/v1/decoder.go b/vendor/github.com/status-im/status-protocol-go/v1/decoder.go new file mode 100644 index 000000000..7e50302a8 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/v1/decoder.go @@ -0,0 +1,125 @@ +package statusproto + +import ( + "errors" + "fmt" + "io" + + "github.com/russolsen/transit" +) + +// NewMessageDecoder returns a new Transit decoder +// that can deserialize Message structs. +// More about Transit: https://github.com/cognitect/transit-format +func NewMessageDecoder(r io.Reader) *transit.Decoder { + decoder := transit.NewDecoder(r) + decoder.AddHandler(messageTag, statusMessageHandler) + decoder.AddHandler(pairMessageTag, pairMessageHandler) + return decoder +} + +const ( + messageTag = "c4" + pairMessageTag = "p2" +) + +func statusMessageHandler(d transit.Decoder, value interface{}) (interface{}, error) { + taggedValue, ok := value.(transit.TaggedValue) + if !ok { + return nil, errors.New("not a tagged value") + } + values, ok := taggedValue.Value.([]interface{}) + if !ok { + return nil, errors.New("tagged value does not contain values") + } + + sm := Message{} + for idx, v := range values { + var ok bool + + switch idx { + case 0: + sm.Text, ok = v.(string) + case 1: + sm.ContentT, ok = v.(string) + case 2: + var messageT transit.Keyword + messageT, ok = v.(transit.Keyword) + if ok { + sm.MessageT = string(messageT) + } + case 3: + sm.Clock, ok = v.(int64) + case 4: + var timestamp int64 + timestamp, ok = v.(int64) + if ok { + sm.Timestamp = TimestampInMs(timestamp) + } + case 5: + var content map[interface{}]interface{} + content, ok = v.(map[interface{}]interface{}) + if !ok { + break + } + + for key, contentVal := range content { + var keyKeyword transit.Keyword + keyKeyword, ok = key.(transit.Keyword) + if !ok { + break + } + + switch keyKeyword { + case transit.Keyword("text"): + sm.Content.Text, ok = contentVal.(string) + case transit.Keyword("chat-id"): + sm.Content.ChatID, ok = contentVal.(string) + } + } + default: + // skip any other values + ok = true + } + + if !ok { + return nil, fmt.Errorf("invalid value for index: %d", idx) + } + } + return sm, nil +} + +func pairMessageHandler(d transit.Decoder, value interface{}) (interface{}, error) { + taggedValue, ok := value.(transit.TaggedValue) + if !ok { + return nil, errors.New("not a tagged value") + } + values, ok := taggedValue.Value.([]interface{}) + if !ok { + return nil, errors.New("tagged value does not contain values") + } + + pm := PairMessage{} + for idx, v := range values { + var ok bool + + switch idx { + case 0: + pm.InstallationID, ok = v.(string) + case 1: + pm.DeviceType, ok = v.(string) + case 2: + pm.Name, ok = v.(string) + case 3: + pm.FCMToken, ok = v.(string) + default: + // skip any other values + ok = true + } + + if !ok { + return nil, fmt.Errorf("invalid value for index: %d", idx) + } + } + return pm, nil +} diff --git a/vendor/github.com/status-im/status-protocol-go/v1/encoder.go b/vendor/github.com/status-im/status-protocol-go/v1/encoder.go new file mode 100644 index 000000000..9f44a620c --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/v1/encoder.go @@ -0,0 +1,65 @@ +package statusproto + +import ( + "errors" + "io" + "reflect" + + "github.com/russolsen/transit" +) + +// NewMessageEncoder returns a new Transit encoder +// that can encode Message values. +// More about Transit: https://github.com/cognitect/transit-format +func NewMessageEncoder(w io.Writer) *transit.Encoder { + encoder := transit.NewEncoder(w, false) + encoder.AddHandler(messageType, defaultMessageValueEncoder) + encoder.AddHandler(pairMessageType, defaultMessageValueEncoder) + return encoder +} + +var ( + messageType = reflect.TypeOf(Message{}) + pairMessageType = reflect.TypeOf(PairMessage{}) + defaultMessageValueEncoder = &messageValueEncoder{} +) + +type messageValueEncoder struct{} + +func (messageValueEncoder) IsStringable(reflect.Value) bool { + return false +} + +func (messageValueEncoder) Encode(e transit.Encoder, value reflect.Value, asString bool) error { + switch message := value.Interface().(type) { + case Message: + taggedValue := transit.TaggedValue{ + Tag: messageTag, + Value: []interface{}{ + message.Text, + message.ContentT, + transit.Keyword(message.MessageT), + message.Clock, + message.Timestamp, + map[interface{}]interface{}{ + transit.Keyword("chat-id"): message.Content.ChatID, + transit.Keyword("text"): message.Content.Text, + }, + }, + } + return e.EncodeInterface(taggedValue, false) + case PairMessage: + taggedValue := transit.TaggedValue{ + Tag: pairMessageTag, + Value: []interface{}{ + message.InstallationID, + message.DeviceType, + message.Name, + message.FCMToken, + }, + } + return e.EncodeInterface(taggedValue, false) + } + + return errors.New("unknown message type to encode") +} diff --git a/vendor/github.com/status-im/status-protocol-go/v1/message.go b/vendor/github.com/status-im/status-protocol-go/v1/message.go new file mode 100644 index 000000000..898ca40a3 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/v1/message.go @@ -0,0 +1,234 @@ +package statusproto + +import ( + "bytes" + "crypto/ecdsa" + "encoding/hex" + "encoding/json" + "errors" + "github.com/ethereum/go-ethereum/crypto" + "github.com/golang/protobuf/proto" + "log" + "strings" + "time" +) + +//go:generate protoc --go_out=. ./message.proto + +const ( + // ContentTypeTextPlain means that the message contains plain text. + ContentTypeTextPlain = "text/plain" +) + +// Message types. +const ( + MessageTypePublicGroup = "public-group-user-message" + MessageTypePrivate = "user-message" + MessageTypePrivateGroup = "group-user-message" +) + +var ( + // ErrInvalidDecodedValue means that the decoded message is of wrong type. + // This might mean that the status message serialization tag changed. + ErrInvalidDecodedValue = errors.New("invalid decoded value type") +) + +// Content contains the chat ID and the actual text of a message. +type Content struct { + ChatID string `json:"chat_id"` + Text string `json:"text"` +} + +// TimestampInMs is a timestamp in milliseconds. +type TimestampInMs int64 + +// Time returns a time.Time instance. +func (t TimestampInMs) Time() time.Time { + ts := int64(t) + seconds := ts / 1000 + return time.Unix(seconds, (ts%1000)*int64(time.Millisecond)) +} + +// TimestampInMsFromTime returns a TimestampInMs from a time.Time instance. +func TimestampInMsFromTime(t time.Time) TimestampInMs { + return TimestampInMs(t.UnixNano() / int64(time.Millisecond)) +} + +// Flags define various boolean properties of a message. +type Flags uint64 + +func (f *Flags) Set(val Flags) { *f = *f | val } +func (f *Flags) Clear(val Flags) { *f = *f &^ val } +func (f *Flags) Toggle(val Flags) { *f = *f ^ val } +func (f Flags) Has(val Flags) bool { return f&val != 0 } + +// A list of Message flags. By default, a message is unread. +const ( + MessageRead Flags = 1 << iota +) + +// StatusMessage is any Status Protocol message. +type StatusMessage struct { + Message interface{} + ID []byte `json:"-"` + SigPubKey *ecdsa.PublicKey `json:"-"` +} + +func (m *StatusMessage) MarshalJSON() ([]byte, error) { + type MessageAlias StatusMessage + item := struct { + *MessageAlias + ID string `json:"id"` + }{ + MessageAlias: (*MessageAlias)(m), + ID: "0x" + hex.EncodeToString(m.ID), + } + + return json.Marshal(item) +} + +// Message is a chat message sent by an user. +type Message struct { + Text string `json:"text"` // TODO: why is this duplicated? + ContentT string `json:"content_type"` + MessageT string `json:"message_type"` + Clock int64 `json:"clock"` // lamport timestamp; see CalcMessageClock for more details + Timestamp TimestampInMs `json:"timestamp"` + Content Content `json:"content"` + + Flags Flags `json:"-"` + ID []byte `json:"-"` + SigPubKey *ecdsa.PublicKey `json:"-"` +} + +func (m *Message) MarshalJSON() ([]byte, error) { + type MessageAlias Message + item := struct { + *MessageAlias + ID string `json:"id"` + }{ + MessageAlias: (*MessageAlias)(m), + ID: "0x" + hex.EncodeToString(m.ID), + } + + return json.Marshal(item) +} + +func (m Message) Unread() bool { + return !m.Flags.Has(MessageRead) +} + +// createTextMessage creates a Message. +func createTextMessage(data []byte, lastClock int64, chatID, messageType string) Message { + text := strings.TrimSpace(string(data)) + ts := TimestampInMsFromTime(time.Now()) + clock := CalcMessageClock(lastClock, ts) + + return Message{ + Text: text, + ContentT: ContentTypeTextPlain, + MessageT: messageType, + Clock: clock, + Timestamp: ts, + Content: Content{ChatID: chatID, Text: text}, + } +} + +// CreatePublicTextMessage creates a public text Message. +func CreatePublicTextMessage(data []byte, lastClock int64, chatID string) Message { + return createTextMessage(data, lastClock, chatID, MessageTypePublicGroup) +} + +// CreatePrivateTextMessage creates a public text Message. +func CreatePrivateTextMessage(data []byte, lastClock int64, chatID string) Message { + return createTextMessage(data, lastClock, chatID, MessageTypePrivate) +} + +func unwrapMessage(data []byte) (*StatusProtocolMessage, error) { + var message StatusProtocolMessage + err := proto.Unmarshal(data, &message) + if err != nil { + return nil, err + } + return &message, nil +} + +func decodeTransitMessage(data []byte) (interface{}, error) { + buf := bytes.NewBuffer(data) + decoder := NewMessageDecoder(buf) + value, err := decoder.Decode() + if err != nil { + return nil, err + } + return value, nil +} + +// DecodeMessage decodes a raw payload to StatusMessage struct. +func DecodeMessage(transportPublicKey *ecdsa.PublicKey, data []byte) (message StatusMessage, err error) { + transitMessage := data + + // Getting a signature from transport message should happen only if + // the signature was not defined in the payload itself. + message.SigPubKey = transportPublicKey + + statusProtocolMessage, err := unwrapMessage(data) + if err == nil { + // Wrapped message, extract transit and signature + transitMessage = statusProtocolMessage.Payload + if statusProtocolMessage.Signature != nil { + recoveredKey, err := crypto.SigToPub( + crypto.Keccak256(transitMessage), + statusProtocolMessage.Signature, + ) + if err != nil { + return message, err + } + + message.SigPubKey = recoveredKey + } + } + + message.ID = MessageID(message.SigPubKey, transitMessage) + value, err := decodeTransitMessage(transitMessage) + if err != nil { + log.Printf("[message::DecodeMessage] could not decode message: %#x", message.ID) + return message, err + } + message.Message = value + + return message, nil +} + +// EncodeMessage encodes a Message using Transit serialization. +func EncodeMessage(value Message) ([]byte, error) { + var buf bytes.Buffer + encoder := NewMessageEncoder(&buf) + if err := encoder.Encode(value); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// MessageID calculates the messageID, by appending the sha3-256 to the compress pubkey +func MessageID(author *ecdsa.PublicKey, data []byte) []byte { + keyBytes := crypto.CompressPubkey(author) + return crypto.Keccak256(append(keyBytes, data...)) +} + +// WrapMessageV1 wraps a payload into a protobuf message and signs it if an identity is provided +func WrapMessageV1(payload []byte, identity *ecdsa.PrivateKey) ([]byte, error) { + var signature []byte + if identity != nil { + var err error + signature, err = crypto.Sign(crypto.Keccak256(payload), identity) + if err != nil { + return nil, err + } + } + + message := &StatusProtocolMessage{ + Signature: signature, + Payload: payload, + } + return proto.Marshal(message) +} diff --git a/vendor/github.com/status-im/status-protocol-go/v1/message.pb.go b/vendor/github.com/status-im/status-protocol-go/v1/message.pb.go new file mode 100644 index 000000000..88a51606e --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/v1/message.pb.go @@ -0,0 +1,85 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: message.proto + +package statusproto + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type StatusProtocolMessage struct { + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StatusProtocolMessage) Reset() { *m = StatusProtocolMessage{} } +func (m *StatusProtocolMessage) String() string { return proto.CompactTextString(m) } +func (*StatusProtocolMessage) ProtoMessage() {} +func (*StatusProtocolMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_33c57e4bae7b9afd, []int{0} +} + +func (m *StatusProtocolMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_StatusProtocolMessage.Unmarshal(m, b) +} +func (m *StatusProtocolMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_StatusProtocolMessage.Marshal(b, m, deterministic) +} +func (m *StatusProtocolMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_StatusProtocolMessage.Merge(m, src) +} +func (m *StatusProtocolMessage) XXX_Size() int { + return xxx_messageInfo_StatusProtocolMessage.Size(m) +} +func (m *StatusProtocolMessage) XXX_DiscardUnknown() { + xxx_messageInfo_StatusProtocolMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_StatusProtocolMessage proto.InternalMessageInfo + +func (m *StatusProtocolMessage) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func (m *StatusProtocolMessage) GetPayload() []byte { + if m != nil { + return m.Payload + } + return nil +} + +func init() { + proto.RegisterType((*StatusProtocolMessage)(nil), "statusproto.StatusProtocolMessage") +} + +func init() { proto.RegisterFile("message.proto", fileDescriptor_33c57e4bae7b9afd) } + +var fileDescriptor_33c57e4bae7b9afd = []byte{ + // 108 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0x4d, 0x2d, 0x2e, + 0x4e, 0x4c, 0x4f, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x2e, 0x2e, 0x49, 0x2c, 0x29, + 0x2d, 0x06, 0x73, 0x94, 0xfc, 0xb9, 0x44, 0x83, 0xc1, 0xdc, 0x00, 0x10, 0x37, 0x39, 0x3f, 0xc7, + 0x17, 0xa2, 0x56, 0x48, 0x86, 0x8b, 0xb3, 0x38, 0x33, 0x3d, 0x2f, 0xb1, 0xa4, 0xb4, 0x28, 0x55, + 0x82, 0x51, 0x81, 0x51, 0x83, 0x27, 0x08, 0x21, 0x20, 0x24, 0xc1, 0xc5, 0x5e, 0x90, 0x58, 0x99, + 0x93, 0x9f, 0x98, 0x22, 0xc1, 0x04, 0x96, 0x83, 0x71, 0x93, 0xd8, 0xc0, 0xe6, 0x1a, 0x03, 0x02, + 0x00, 0x00, 0xff, 0xff, 0xda, 0x9a, 0x88, 0x4b, 0x75, 0x00, 0x00, 0x00, +} diff --git a/vendor/github.com/status-im/status-protocol-go/v1/message.proto b/vendor/github.com/status-im/status-protocol-go/v1/message.proto new file mode 100644 index 000000000..4c1d6cc91 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/v1/message.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package statusproto; + +message StatusProtocolMessage { + bytes signature = 1; + bytes payload = 2; +} diff --git a/vendor/github.com/status-im/status-protocol-go/v1/pair_message.go b/vendor/github.com/status-im/status-protocol-go/v1/pair_message.go new file mode 100644 index 000000000..7510b97c2 --- /dev/null +++ b/vendor/github.com/status-im/status-protocol-go/v1/pair_message.go @@ -0,0 +1,71 @@ +package statusproto + +import ( + "bytes" + "encoding/hex" + "encoding/json" +) + +// PairMessage contains all message details. +type PairMessage struct { + InstallationID string `json:"installationId"` + // The type of the device + DeviceType string `json:"deviceType"` + // Name the user set name + Name string `json:"name"` + // The FCMToken for mobile platforms + FCMToken string `json:"fcmToken"` + + // not protocol defined fields + ID []byte `json:"-"` +} + +func (m *PairMessage) MarshalJSON() ([]byte, error) { + type PairMessageAlias PairMessage + item := struct { + *PairMessageAlias + ID string `json:"id"` + }{ + PairMessageAlias: (*PairMessageAlias)(m), + ID: "0x" + hex.EncodeToString(m.ID), + } + + return json.Marshal(item) +} + +// CreatePairMessage creates a PairMessage which is used +// to pair devices. +func CreatePairMessage(installationID string, name string, deviceType string, fcmToken string) PairMessage { + return PairMessage{ + InstallationID: installationID, + Name: name, + DeviceType: deviceType, + FCMToken: fcmToken, + } +} + +// DecodePairMessage decodes a raw payload to Message struct. +func DecodePairMessage(data []byte) (message PairMessage, err error) { + buf := bytes.NewBuffer(data) + decoder := NewMessageDecoder(buf) + value, err := decoder.Decode() + if err != nil { + return + } + + message, ok := value.(PairMessage) + if !ok { + return message, ErrInvalidDecodedValue + } + return +} + +// EncodePairMessage encodes a PairMessage using Transit serialization. +func EncodePairMessage(value PairMessage) ([]byte, error) { + var buf bytes.Buffer + encoder := NewMessageEncoder(&buf) + if err := encoder.Encode(value); err != nil { + return nil, err + } + return buf.Bytes(), nil +} diff --git a/vendor/golang.org/x/crypto/ed25519/ed25519.go b/vendor/golang.org/x/crypto/ed25519/ed25519.go index d6f683ba3..c7f8c7e64 100644 --- a/vendor/golang.org/x/crypto/ed25519/ed25519.go +++ b/vendor/golang.org/x/crypto/ed25519/ed25519.go @@ -2,6 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// In Go 1.13, the ed25519 package was promoted to the standard library as +// crypto/ed25519, and this package became a wrapper for the standard library one. +// +// +build !go1.13 + // Package ed25519 implements the Ed25519 signature algorithm. See // https://ed25519.cr.yp.to/. // diff --git a/vendor/golang.org/x/crypto/ed25519/ed25519_go113.go b/vendor/golang.org/x/crypto/ed25519/ed25519_go113.go new file mode 100644 index 000000000..d1448d8d2 --- /dev/null +++ b/vendor/golang.org/x/crypto/ed25519/ed25519_go113.go @@ -0,0 +1,73 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.13 + +// Package ed25519 implements the Ed25519 signature algorithm. See +// https://ed25519.cr.yp.to/. +// +// These functions are also compatible with the “Ed25519” function defined in +// RFC 8032. However, unlike RFC 8032's formulation, this package's private key +// representation includes a public key suffix to make multiple signing +// operations with the same key more efficient. This package refers to the RFC +// 8032 private key as the “seed”. +// +// Beginning with Go 1.13, the functionality of this package was moved to the +// standard library as crypto/ed25519. This package only acts as a compatibility +// wrapper. +package ed25519 + +import ( + "crypto/ed25519" + "io" +) + +const ( + // PublicKeySize is the size, in bytes, of public keys as used in this package. + PublicKeySize = 32 + // PrivateKeySize is the size, in bytes, of private keys as used in this package. + PrivateKeySize = 64 + // SignatureSize is the size, in bytes, of signatures generated and verified by this package. + SignatureSize = 64 + // SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032. + SeedSize = 32 +) + +// PublicKey is the type of Ed25519 public keys. +// +// This type is an alias for crypto/ed25519's PublicKey type. +// See the crypto/ed25519 package for the methods on this type. +type PublicKey = ed25519.PublicKey + +// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer. +// +// This type is an alias for crypto/ed25519's PrivateKey type. +// See the crypto/ed25519 package for the methods on this type. +type PrivateKey = ed25519.PrivateKey + +// GenerateKey generates a public/private key pair using entropy from rand. +// If rand is nil, crypto/rand.Reader will be used. +func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { + return ed25519.GenerateKey(rand) +} + +// NewKeyFromSeed calculates a private key from a seed. It will panic if +// len(seed) is not SeedSize. This function is provided for interoperability +// with RFC 8032. RFC 8032's private keys correspond to seeds in this +// package. +func NewKeyFromSeed(seed []byte) PrivateKey { + return ed25519.NewKeyFromSeed(seed) +} + +// Sign signs the message with privateKey and returns a signature. It will +// panic if len(privateKey) is not PrivateKeySize. +func Sign(privateKey PrivateKey, message []byte) []byte { + return ed25519.Sign(privateKey, message) +} + +// Verify reports whether sig is a valid signature of message by publicKey. It +// will panic if len(publicKey) is not PublicKeySize. +func Verify(publicKey PublicKey, message, sig []byte) bool { + return ed25519.Verify(publicKey, message, sig) +} diff --git a/vendor/golang.org/x/net/bpf/vm_instructions.go b/vendor/golang.org/x/net/bpf/vm_instructions.go index f0d2e55bd..cf8947c33 100644 --- a/vendor/golang.org/x/net/bpf/vm_instructions.go +++ b/vendor/golang.org/x/net/bpf/vm_instructions.go @@ -129,7 +129,8 @@ func loadIndirect(ins LoadIndirect, in []byte, regX uint32) (uint32, bool) { func loadMemShift(ins LoadMemShift, in []byte) (uint32, bool) { offset := int(ins.Off) - if !inBounds(len(in), offset, 0) { + // Size of LoadMemShift is always 1 byte + if !inBounds(len(in), offset, 1) { return 0, false } diff --git a/vendor/golang.org/x/net/html/parse.go b/vendor/golang.org/x/net/html/parse.go index e0bfc1f36..992cff2a3 100644 --- a/vendor/golang.org/x/net/html/parse.go +++ b/vendor/golang.org/x/net/html/parse.go @@ -2300,6 +2300,33 @@ func (p *parser) parse() error { // // The input is assumed to be UTF-8 encoded. func Parse(r io.Reader) (*Node, error) { + return ParseWithOptions(r) +} + +// ParseFragment parses a fragment of HTML and returns the nodes that were +// found. If the fragment is the InnerHTML for an existing element, pass that +// element in context. +// +// It has the same intricacies as Parse. +func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { + return ParseFragmentWithOptions(r, context) +} + +// ParseOption configures a parser. +type ParseOption func(p *parser) + +// ParseOptionEnableScripting configures the scripting flag. +// https://html.spec.whatwg.org/multipage/webappapis.html#enabling-and-disabling-scripting +// +// By default, scripting is enabled. +func ParseOptionEnableScripting(enable bool) ParseOption { + return func(p *parser) { + p.scripting = enable + } +} + +// ParseWithOptions is like Parse, with options. +func ParseWithOptions(r io.Reader, opts ...ParseOption) (*Node, error) { p := &parser{ tokenizer: NewTokenizer(r), doc: &Node{ @@ -2309,6 +2336,11 @@ func Parse(r io.Reader) (*Node, error) { framesetOK: true, im: initialIM, } + + for _, f := range opts { + f(p) + } + err := p.parse() if err != nil { return nil, err @@ -2316,12 +2348,8 @@ func Parse(r io.Reader) (*Node, error) { return p.doc, nil } -// ParseFragment parses a fragment of HTML and returns the nodes that were -// found. If the fragment is the InnerHTML for an existing element, pass that -// element in context. -// -// It has the same intricacies as Parse. -func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { +// ParseFragmentWithOptions is like ParseFragment, with options. +func ParseFragmentWithOptions(r io.Reader, context *Node, opts ...ParseOption) ([]*Node, error) { contextTag := "" if context != nil { if context.Type != ElementNode { @@ -2345,6 +2373,10 @@ func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { context: context, } + for _, f := range opts { + f(p) + } + root := &Node{ Type: ElementNode, DataAtom: a.Html, diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go index 63f0534fa..27be0efac 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build arm64 amd64 ppc64 ppc64le mips64 mips64le s390x +// +build arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x // +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/defs_aix.go b/vendor/golang.org/x/net/internal/socket/defs_aix.go index ed6f8071c..c9d05b261 100644 --- a/vendor/golang.org/x/net/internal/socket/defs_aix.go +++ b/vendor/golang.org/x/net/internal/socket/defs_aix.go @@ -16,14 +16,6 @@ package socket */ import "C" -const ( - sysAF_UNSPEC = C.AF_UNSPEC - sysAF_INET = C.AF_INET - sysAF_INET6 = C.AF_INET6 - - sysSOCK_RAW = C.SOCK_RAW -) - type iovec C.struct_iovec type msghdr C.struct_msghdr diff --git a/vendor/golang.org/x/net/internal/socket/defs_darwin.go b/vendor/golang.org/x/net/internal/socket/defs_darwin.go index 14e28c0b4..b780bc67a 100644 --- a/vendor/golang.org/x/net/internal/socket/defs_darwin.go +++ b/vendor/golang.org/x/net/internal/socket/defs_darwin.go @@ -16,14 +16,6 @@ package socket */ import "C" -const ( - sysAF_UNSPEC = C.AF_UNSPEC - sysAF_INET = C.AF_INET - sysAF_INET6 = C.AF_INET6 - - sysSOCK_RAW = C.SOCK_RAW -) - type iovec C.struct_iovec type msghdr C.struct_msghdr diff --git a/vendor/golang.org/x/net/internal/socket/defs_dragonfly.go b/vendor/golang.org/x/net/internal/socket/defs_dragonfly.go index 14e28c0b4..b780bc67a 100644 --- a/vendor/golang.org/x/net/internal/socket/defs_dragonfly.go +++ b/vendor/golang.org/x/net/internal/socket/defs_dragonfly.go @@ -16,14 +16,6 @@ package socket */ import "C" -const ( - sysAF_UNSPEC = C.AF_UNSPEC - sysAF_INET = C.AF_INET - sysAF_INET6 = C.AF_INET6 - - sysSOCK_RAW = C.SOCK_RAW -) - type iovec C.struct_iovec type msghdr C.struct_msghdr diff --git a/vendor/golang.org/x/net/internal/socket/defs_freebsd.go b/vendor/golang.org/x/net/internal/socket/defs_freebsd.go index 14e28c0b4..b780bc67a 100644 --- a/vendor/golang.org/x/net/internal/socket/defs_freebsd.go +++ b/vendor/golang.org/x/net/internal/socket/defs_freebsd.go @@ -16,14 +16,6 @@ package socket */ import "C" -const ( - sysAF_UNSPEC = C.AF_UNSPEC - sysAF_INET = C.AF_INET - sysAF_INET6 = C.AF_INET6 - - sysSOCK_RAW = C.SOCK_RAW -) - type iovec C.struct_iovec type msghdr C.struct_msghdr diff --git a/vendor/golang.org/x/net/internal/socket/defs_linux.go b/vendor/golang.org/x/net/internal/socket/defs_linux.go index ce9ec2f6d..6c5c11dcc 100644 --- a/vendor/golang.org/x/net/internal/socket/defs_linux.go +++ b/vendor/golang.org/x/net/internal/socket/defs_linux.go @@ -18,14 +18,6 @@ package socket */ import "C" -const ( - sysAF_UNSPEC = C.AF_UNSPEC - sysAF_INET = C.AF_INET - sysAF_INET6 = C.AF_INET6 - - sysSOCK_RAW = C.SOCK_RAW -) - type iovec C.struct_iovec type msghdr C.struct_msghdr diff --git a/vendor/golang.org/x/net/internal/socket/defs_netbsd.go b/vendor/golang.org/x/net/internal/socket/defs_netbsd.go index 3f8433569..3d3b77639 100644 --- a/vendor/golang.org/x/net/internal/socket/defs_netbsd.go +++ b/vendor/golang.org/x/net/internal/socket/defs_netbsd.go @@ -16,14 +16,6 @@ package socket */ import "C" -const ( - sysAF_UNSPEC = C.AF_UNSPEC - sysAF_INET = C.AF_INET - sysAF_INET6 = C.AF_INET6 - - sysSOCK_RAW = C.SOCK_RAW -) - type iovec C.struct_iovec type msghdr C.struct_msghdr diff --git a/vendor/golang.org/x/net/internal/socket/defs_openbsd.go b/vendor/golang.org/x/net/internal/socket/defs_openbsd.go index 14e28c0b4..b780bc67a 100644 --- a/vendor/golang.org/x/net/internal/socket/defs_openbsd.go +++ b/vendor/golang.org/x/net/internal/socket/defs_openbsd.go @@ -16,14 +16,6 @@ package socket */ import "C" -const ( - sysAF_UNSPEC = C.AF_UNSPEC - sysAF_INET = C.AF_INET - sysAF_INET6 = C.AF_INET6 - - sysSOCK_RAW = C.SOCK_RAW -) - type iovec C.struct_iovec type msghdr C.struct_msghdr diff --git a/vendor/golang.org/x/net/internal/socket/defs_solaris.go b/vendor/golang.org/x/net/internal/socket/defs_solaris.go index 14e28c0b4..b780bc67a 100644 --- a/vendor/golang.org/x/net/internal/socket/defs_solaris.go +++ b/vendor/golang.org/x/net/internal/socket/defs_solaris.go @@ -16,14 +16,6 @@ package socket */ import "C" -const ( - sysAF_UNSPEC = C.AF_UNSPEC - sysAF_INET = C.AF_INET - sysAF_INET6 = C.AF_INET6 - - sysSOCK_RAW = C.SOCK_RAW -) - type iovec C.struct_iovec type msghdr C.struct_msghdr diff --git a/vendor/golang.org/x/net/internal/socket/iovec_64bit.go b/vendor/golang.org/x/net/internal/socket/iovec_64bit.go index 07738825f..dfeda752b 100644 --- a/vendor/golang.org/x/net/internal/socket/iovec_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/iovec_64bit.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build arm64 amd64 ppc64 ppc64le mips64 mips64le s390x +// +build arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x // +build aix darwin dragonfly freebsd linux netbsd openbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go b/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go index 610fc4f3b..e731833a2 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build arm64 amd64 ppc64 ppc64le mips64 mips64le s390x +// +build arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x // +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_const_unix.go b/vendor/golang.org/x/net/internal/socket/sys_const_unix.go new file mode 100644 index 000000000..43797d6e5 --- /dev/null +++ b/vendor/golang.org/x/net/internal/socket/sys_const_unix.go @@ -0,0 +1,17 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris + +package socket + +import "golang.org/x/sys/unix" + +const ( + sysAF_UNSPEC = unix.AF_UNSPEC + sysAF_INET = unix.AF_INET + sysAF_INET6 = unix.AF_INET6 + + sysSOCK_RAW = unix.SOCK_RAW +) diff --git a/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go b/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go new file mode 100644 index 000000000..64f69f1dc --- /dev/null +++ b/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go @@ -0,0 +1,12 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build riscv64 + +package socket + +const ( + sysRECVMMSG = 0xf3 + sysSENDMMSG = 0x10d +) diff --git a/vendor/golang.org/x/net/internal/socket/sys_posix.go b/vendor/golang.org/x/net/internal/socket/sys_posix.go index 24fbb6967..22eae809c 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_posix.go +++ b/vendor/golang.org/x/net/internal/socket/sys_posix.go @@ -33,7 +33,7 @@ func marshalSockaddr(ip net.IP, port int, zone string) []byte { if ip4 := ip.To4(); ip4 != nil { b := make([]byte, sizeofSockaddrInet) switch runtime.GOOS { - case "android", "linux", "solaris", "windows": + case "android", "illumos", "linux", "solaris", "windows": NativeEndian.PutUint16(b[:2], uint16(sysAF_INET)) default: b[0] = sizeofSockaddrInet @@ -46,7 +46,7 @@ func marshalSockaddr(ip net.IP, port int, zone string) []byte { if ip6 := ip.To16(); ip6 != nil && ip.To4() == nil { b := make([]byte, sizeofSockaddrInet6) switch runtime.GOOS { - case "android", "linux", "solaris", "windows": + case "android", "illumos", "linux", "solaris", "windows": NativeEndian.PutUint16(b[:2], uint16(sysAF_INET6)) default: b[0] = sizeofSockaddrInet6 @@ -68,7 +68,7 @@ func parseInetAddr(b []byte, network string) (net.Addr, error) { } var af int switch runtime.GOOS { - case "android", "linux", "solaris", "windows": + case "android", "illumos", "linux", "solaris", "windows": af = int(NativeEndian.Uint16(b[:2])) default: af = int(b[1]) diff --git a/vendor/golang.org/x/net/internal/socket/sys_windows.go b/vendor/golang.org/x/net/internal/socket/sys_windows.go index 924e327bc..d556a4461 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_windows.go +++ b/vendor/golang.org/x/net/internal/socket/sys_windows.go @@ -7,6 +7,8 @@ package socket import ( "syscall" "unsafe" + + "golang.org/x/sys/windows" ) func probeProtocolStack() int { @@ -15,11 +17,11 @@ func probeProtocolStack() int { } const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x17 + sysAF_UNSPEC = windows.AF_UNSPEC + sysAF_INET = windows.AF_INET + sysAF_INET6 = windows.AF_INET6 - sysSOCK_RAW = 0x3 + sysSOCK_RAW = windows.SOCK_RAW ) type sockaddrInet struct { diff --git a/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go b/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go index 3a825f47d..813385a98 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go @@ -6,14 +6,6 @@ package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x18 - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint64 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_darwin_386.go b/vendor/golang.org/x/net/internal/socket/zsys_darwin_386.go index 26f8feff3..083bda51c 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_darwin_386.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_darwin_386.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_darwin.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x1e - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint32 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_darwin_amd64.go b/vendor/golang.org/x/net/internal/socket/zsys_darwin_amd64.go index e2987f7db..55c6c9f57 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_darwin_amd64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_darwin_amd64.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_darwin.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x1e - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint64 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_darwin_arm.go b/vendor/golang.org/x/net/internal/socket/zsys_darwin_arm.go index 26f8feff3..083bda51c 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_darwin_arm.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_darwin_arm.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_darwin.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x1e - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint32 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_darwin_arm64.go b/vendor/golang.org/x/net/internal/socket/zsys_darwin_arm64.go index e2987f7db..55c6c9f57 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_darwin_arm64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_darwin_arm64.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_darwin.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x1e - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint64 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_dragonfly_amd64.go b/vendor/golang.org/x/net/internal/socket/zsys_dragonfly_amd64.go index c582abd57..8b7d161d7 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_dragonfly_amd64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_dragonfly_amd64.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_dragonfly.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x1c - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint64 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_freebsd_386.go b/vendor/golang.org/x/net/internal/socket/zsys_freebsd_386.go index 04a24886c..3e71ff574 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_freebsd_386.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_freebsd_386.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_freebsd.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x1c - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint32 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_freebsd_amd64.go b/vendor/golang.org/x/net/internal/socket/zsys_freebsd_amd64.go index 35c7cb9c9..238d90de6 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_freebsd_amd64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_freebsd_amd64.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_freebsd.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x1c - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint64 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_freebsd_arm.go b/vendor/golang.org/x/net/internal/socket/zsys_freebsd_arm.go index 04a24886c..3e71ff574 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_freebsd_arm.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_freebsd_arm.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_freebsd.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x1c - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint32 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_freebsd_arm64.go b/vendor/golang.org/x/net/internal/socket/zsys_freebsd_arm64.go new file mode 100644 index 000000000..238d90de6 --- /dev/null +++ b/vendor/golang.org/x/net/internal/socket/zsys_freebsd_arm64.go @@ -0,0 +1,53 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs defs_freebsd.go + +package socket + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen int32 + Pad_cgo_1 [4]byte + Control *byte + Controllen uint32 + Flags int32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x30 + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_386.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_386.go index 430206930..72d8b2542 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_386.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_386.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0xa - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint32 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_amd64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_amd64.go index 1502f6c55..3545319ae 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_amd64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_amd64.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0xa - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint64 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_arm.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_arm.go index 430206930..72d8b2542 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_arm.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_arm.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0xa - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint32 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_arm64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_arm64.go index 1502f6c55..3545319ae 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_arm64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_arm64.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0xa - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint64 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_mips.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_mips.go index 430206930..72d8b2542 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_mips.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_mips.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0xa - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint32 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64.go index 1502f6c55..3545319ae 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0xa - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint64 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64le.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64le.go index 1502f6c55..3545319ae 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64le.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64le.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0xa - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint64 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_mipsle.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_mipsle.go index 430206930..72d8b2542 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_mipsle.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_mipsle.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0xa - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint32 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64.go index 1502f6c55..3545319ae 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0xa - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint64 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64le.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64le.go index 1502f6c55..3545319ae 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64le.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64le.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0xa - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint64 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go new file mode 100644 index 000000000..dbff234fb --- /dev/null +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go @@ -0,0 +1,59 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs defs_linux.go + +// +build riscv64 + +package socket + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Iov *iovec + Iovlen uint64 + Control *byte + Controllen uint64 + Flags int32 + Pad_cgo_0 [4]byte +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 + Pad_cgo_0 [4]byte +} + +type cmsghdr struct { + Len uint64 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 + sizeofCmsghdr = 0x10 + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_s390x.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_s390x.go index 1502f6c55..3545319ae 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_s390x.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_s390x.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0xa - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint64 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_386.go b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_386.go index db60491fe..bf8f47c88 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_386.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_386.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_netbsd.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x18 - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint32 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_amd64.go b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_amd64.go index 2a1a79985..a46eff991 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_amd64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_amd64.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_netbsd.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x18 - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint64 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm.go b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm.go index db60491fe..bf8f47c88 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_netbsd.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x18 - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint32 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm64.go b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm64.go new file mode 100644 index 000000000..a46eff991 --- /dev/null +++ b/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm64.go @@ -0,0 +1,60 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs defs_netbsd.go + +package socket + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen int32 + Pad_cgo_1 [4]byte + Control *byte + Controllen uint32 + Flags int32 +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 + Pad_cgo_0 [4]byte +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x30 + sizeofMmsghdr = 0x40 + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/golang.org/x/net/internal/socket/zsys_openbsd_386.go b/vendor/golang.org/x/net/internal/socket/zsys_openbsd_386.go index 1c836361e..73655a14c 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_openbsd_386.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_openbsd_386.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_openbsd.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x18 - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint32 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_openbsd_amd64.go b/vendor/golang.org/x/net/internal/socket/zsys_openbsd_amd64.go index a6c0bf464..0a4de80f2 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_openbsd_amd64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_openbsd_amd64.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_openbsd.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x18 - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint64 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_openbsd_arm.go b/vendor/golang.org/x/net/internal/socket/zsys_openbsd_arm.go index 1c836361e..73655a14c 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_openbsd_arm.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_openbsd_arm.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_openbsd.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x18 - - sysSOCK_RAW = 0x3 -) - type iovec struct { Base *byte Len uint32 diff --git a/vendor/golang.org/x/net/internal/socket/zsys_openbsd_arm64.go b/vendor/golang.org/x/net/internal/socket/zsys_openbsd_arm64.go new file mode 100644 index 000000000..0a4de80f2 --- /dev/null +++ b/vendor/golang.org/x/net/internal/socket/zsys_openbsd_arm64.go @@ -0,0 +1,53 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs defs_openbsd.go + +package socket + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen uint32 + Pad_cgo_1 [4]byte + Control *byte + Controllen uint32 + Flags int32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x30 + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/golang.org/x/net/internal/socket/zsys_solaris_amd64.go b/vendor/golang.org/x/net/internal/socket/zsys_solaris_amd64.go index 327c63290..353cd5fb4 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_solaris_amd64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_solaris_amd64.go @@ -1,16 +1,8 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_solaris.go package socket -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x1a - - sysSOCK_RAW = 0x4 -) - type iovec struct { Base *int8 Len uint64 diff --git a/vendor/golang.org/x/net/ipv4/zsys_darwin.go b/vendor/golang.org/x/net/ipv4/zsys_darwin.go index c07cc883f..e05a251ba 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_darwin.go +++ b/vendor/golang.org/x/net/ipv4/zsys_darwin.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_darwin.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_dragonfly.go b/vendor/golang.org/x/net/ipv4/zsys_dragonfly.go index c4365e9e7..6d65e9fcb 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_dragonfly.go +++ b/vendor/golang.org/x/net/ipv4/zsys_dragonfly.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_dragonfly.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_freebsd_386.go b/vendor/golang.org/x/net/ipv4/zsys_freebsd_386.go index 8c4aec94c..136e2b8f1 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_freebsd_386.go +++ b/vendor/golang.org/x/net/ipv4/zsys_freebsd_386.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_freebsd.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_freebsd_amd64.go b/vendor/golang.org/x/net/ipv4/zsys_freebsd_amd64.go index 4b10b7c57..4f730f19e 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_freebsd_amd64.go +++ b/vendor/golang.org/x/net/ipv4/zsys_freebsd_amd64.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_freebsd.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_freebsd_arm.go b/vendor/golang.org/x/net/ipv4/zsys_freebsd_arm.go index 4b10b7c57..4f730f19e 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_freebsd_arm.go +++ b/vendor/golang.org/x/net/ipv4/zsys_freebsd_arm.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_freebsd.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_386.go b/vendor/golang.org/x/net/ipv4/zsys_linux_386.go index c0260f0ce..43ef8e592 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_386.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_386.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_amd64.go b/vendor/golang.org/x/net/ipv4/zsys_linux_amd64.go index 9c967eaa6..ee8204da4 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_amd64.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_amd64.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_arm.go b/vendor/golang.org/x/net/ipv4/zsys_linux_arm.go index c0260f0ce..43ef8e592 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_arm.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_arm.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_arm64.go b/vendor/golang.org/x/net/ipv4/zsys_linux_arm64.go index 9c967eaa6..ee8204da4 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_arm64.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_arm64.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_mips.go b/vendor/golang.org/x/net/ipv4/zsys_linux_mips.go index c0260f0ce..43ef8e592 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_mips.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_mips.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_mips64.go b/vendor/golang.org/x/net/ipv4/zsys_linux_mips64.go index 9c967eaa6..ee8204da4 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_mips64.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_mips64.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_mips64le.go b/vendor/golang.org/x/net/ipv4/zsys_linux_mips64le.go index 9c967eaa6..ee8204da4 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_mips64le.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_mips64le.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_mipsle.go b/vendor/golang.org/x/net/ipv4/zsys_linux_mipsle.go index c0260f0ce..43ef8e592 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_mipsle.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_mipsle.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_ppc.go b/vendor/golang.org/x/net/ipv4/zsys_linux_ppc.go index f65bd9a7a..fa1b6bc61 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_ppc.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_ppc.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_ppc64.go b/vendor/golang.org/x/net/ipv4/zsys_linux_ppc64.go index 9c967eaa6..ee8204da4 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_ppc64.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_ppc64.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_ppc64le.go b/vendor/golang.org/x/net/ipv4/zsys_linux_ppc64le.go index 9c967eaa6..ee8204da4 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_ppc64le.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_ppc64le.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go b/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go new file mode 100644 index 000000000..0c0d48012 --- /dev/null +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go @@ -0,0 +1,151 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs defs_linux.go + +// +build riscv64 + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPFilter = 0x4 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_s390x.go b/vendor/golang.org/x/net/ipv4/zsys_linux_s390x.go index 9c967eaa6..ee8204da4 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_s390x.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_s390x.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_linux.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_netbsd.go b/vendor/golang.org/x/net/ipv4/zsys_netbsd.go index fd3624d93..8cfc648ad 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_netbsd.go +++ b/vendor/golang.org/x/net/ipv4/zsys_netbsd.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_netbsd.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_openbsd.go b/vendor/golang.org/x/net/ipv4/zsys_openbsd.go index 12f36be75..37629cb0a 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_openbsd.go +++ b/vendor/golang.org/x/net/ipv4/zsys_openbsd.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_openbsd.go package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_solaris.go b/vendor/golang.org/x/net/ipv4/zsys_solaris.go index 0a3875cc4..cb80a308b 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_solaris.go +++ b/vendor/golang.org/x/net/ipv4/zsys_solaris.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs defs_solaris.go package ipv4 diff --git a/vendor/gopkg.in/go-playground/validator.v9/Makefile b/vendor/gopkg.in/go-playground/validator.v9/Makefile new file mode 100644 index 000000000..aeeee9da9 --- /dev/null +++ b/vendor/gopkg.in/go-playground/validator.v9/Makefile @@ -0,0 +1,16 @@ +GOCMD=go + +linters-install: + $(GOCMD) get -u github.com/alecthomas/gometalinter + gometalinter --install + +lint: linters-install + gometalinter --vendor --disable-all --enable=vet --enable=vetshadow --enable=golint --enable=maligned --enable=megacheck --enable=ineffassign --enable=misspell --enable=errcheck --enable=goconst ./... + +test: + $(GOCMD) test -cover -race ./... + +bench: + $(GOCMD) test -bench=. -benchmem ./... + +.PHONY: test lint linters-install \ No newline at end of file diff --git a/vendor/gopkg.in/go-playground/validator.v9/README.md b/vendor/gopkg.in/go-playground/validator.v9/README.md index e7837254b..8cb94d465 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/README.md +++ b/vendor/gopkg.in/go-playground/validator.v9/README.md @@ -1,7 +1,7 @@ Package validator ================ [![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -![Project status](https://img.shields.io/badge/version-9.9.3-green.svg) +![Project status](https://img.shields.io/badge/version-9.29.0-green.svg) [![Build Status](https://semaphoreci.com/api/v1/joeybloggs/validator/branches/v9/badge.svg)](https://semaphoreci.com/joeybloggs/validator) [![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=v9&service=github)](https://coveralls.io/github/go-playground/validator?branch=v9) [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator) @@ -66,69 +66,73 @@ Please see http://godoc.org/gopkg.in/go-playground/validator.v9 for detailed usa Benchmarks ------ -###### Run on MacBook Pro (15-inch, 2017) Go version go1.9.2 darwin/amd64 +###### Run on MacBook Pro (15-inch, 2017) go version go1.10.2 darwin/amd64 ```go -go test -bench=. -benchmem=true -BenchmarkFieldSuccess-8 20000000 79.9 ns/op 0 B/op 0 allocs/op -BenchmarkFieldSuccessParallel-8 50000000 25.0 ns/op 0 B/op 0 allocs/op -BenchmarkFieldFailure-8 5000000 281 ns/op 208 B/op 4 allocs/op -BenchmarkFieldFailureParallel-8 20000000 97.0 ns/op 208 B/op 4 allocs/op -BenchmarkFieldArrayDiveSuccess-8 3000000 591 ns/op 201 B/op 11 allocs/op -BenchmarkFieldArrayDiveSuccessParallel-8 10000000 195 ns/op 201 B/op 11 allocs/op -BenchmarkFieldArrayDiveFailure-8 2000000 878 ns/op 412 B/op 16 allocs/op -BenchmarkFieldArrayDiveFailureParallel-8 5000000 274 ns/op 413 B/op 16 allocs/op -BenchmarkFieldMapDiveSuccess-8 1000000 1279 ns/op 432 B/op 18 allocs/op -BenchmarkFieldMapDiveSuccessParallel-8 5000000 401 ns/op 432 B/op 18 allocs/op -BenchmarkFieldMapDiveFailure-8 1000000 1060 ns/op 512 B/op 16 allocs/op -BenchmarkFieldMapDiveFailureParallel-8 5000000 334 ns/op 512 B/op 16 allocs/op -BenchmarkFieldMapDiveWithKeysSuccess-8 1000000 1462 ns/op 480 B/op 21 allocs/op -BenchmarkFieldMapDiveWithKeysSuccessParallel-8 3000000 463 ns/op 480 B/op 21 allocs/op -BenchmarkFieldMapDiveWithKeysFailure-8 1000000 1414 ns/op 721 B/op 21 allocs/op -BenchmarkFieldMapDiveWithKeysFailureParallel-8 3000000 446 ns/op 721 B/op 21 allocs/op -BenchmarkFieldCustomTypeSuccess-8 10000000 211 ns/op 32 B/op 2 allocs/op -BenchmarkFieldCustomTypeSuccessParallel-8 20000000 65.9 ns/op 32 B/op 2 allocs/op -BenchmarkFieldCustomTypeFailure-8 5000000 270 ns/op 208 B/op 4 allocs/op -BenchmarkFieldCustomTypeFailureParallel-8 20000000 93.3 ns/op 208 B/op 4 allocs/op -BenchmarkFieldOrTagSuccess-8 2000000 729 ns/op 16 B/op 1 allocs/op -BenchmarkFieldOrTagSuccessParallel-8 5000000 367 ns/op 16 B/op 1 allocs/op -BenchmarkFieldOrTagFailure-8 3000000 472 ns/op 224 B/op 5 allocs/op -BenchmarkFieldOrTagFailureParallel-8 5000000 373 ns/op 224 B/op 5 allocs/op -BenchmarkStructLevelValidationSuccess-8 10000000 201 ns/op 32 B/op 2 allocs/op -BenchmarkStructLevelValidationSuccessParallel-8 20000000 66.3 ns/op 32 B/op 2 allocs/op -BenchmarkStructLevelValidationFailure-8 3000000 468 ns/op 304 B/op 8 allocs/op -BenchmarkStructLevelValidationFailureParallel-8 10000000 172 ns/op 304 B/op 8 allocs/op -BenchmarkStructSimpleCustomTypeSuccess-8 5000000 376 ns/op 32 B/op 2 allocs/op -BenchmarkStructSimpleCustomTypeSuccessParallel-8 20000000 126 ns/op 32 B/op 2 allocs/op -BenchmarkStructSimpleCustomTypeFailure-8 2000000 646 ns/op 424 B/op 9 allocs/op -BenchmarkStructSimpleCustomTypeFailureParallel-8 10000000 240 ns/op 440 B/op 10 allocs/op -BenchmarkStructFilteredSuccess-8 3000000 582 ns/op 288 B/op 9 allocs/op -BenchmarkStructFilteredSuccessParallel-8 10000000 198 ns/op 288 B/op 9 allocs/op -BenchmarkStructFilteredFailure-8 3000000 447 ns/op 256 B/op 7 allocs/op -BenchmarkStructFilteredFailureParallel-8 10000000 156 ns/op 256 B/op 7 allocs/op -BenchmarkStructPartialSuccess-8 3000000 536 ns/op 256 B/op 6 allocs/op -BenchmarkStructPartialSuccessParallel-8 10000000 175 ns/op 256 B/op 6 allocs/op -BenchmarkStructPartialFailure-8 2000000 738 ns/op 480 B/op 11 allocs/op -BenchmarkStructPartialFailureParallel-8 5000000 256 ns/op 480 B/op 11 allocs/op -BenchmarkStructExceptSuccess-8 2000000 835 ns/op 496 B/op 12 allocs/op -BenchmarkStructExceptSuccessParallel-8 10000000 163 ns/op 240 B/op 5 allocs/op -BenchmarkStructExceptFailure-8 2000000 682 ns/op 464 B/op 10 allocs/op -BenchmarkStructExceptFailureParallel-8 10000000 244 ns/op 464 B/op 10 allocs/op -BenchmarkStructSimpleCrossFieldSuccess-8 5000000 392 ns/op 72 B/op 3 allocs/op -BenchmarkStructSimpleCrossFieldSuccessParallel-8 20000000 126 ns/op 72 B/op 3 allocs/op -BenchmarkStructSimpleCrossFieldFailure-8 2000000 611 ns/op 304 B/op 8 allocs/op -BenchmarkStructSimpleCrossFieldFailureParallel-8 10000000 214 ns/op 304 B/op 8 allocs/op -BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 3000000 567 ns/op 80 B/op 4 allocs/op -BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 177 ns/op 80 B/op 4 allocs/op -BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 807 ns/op 320 B/op 9 allocs/op -BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 268 ns/op 320 B/op 9 allocs/op -BenchmarkStructSimpleSuccess-8 5000000 256 ns/op 0 B/op 0 allocs/op -BenchmarkStructSimpleSuccessParallel-8 20000000 76.3 ns/op 0 B/op 0 allocs/op -BenchmarkStructSimpleFailure-8 2000000 625 ns/op 424 B/op 9 allocs/op -BenchmarkStructSimpleFailureParallel-8 10000000 219 ns/op 424 B/op 9 allocs/op -BenchmarkStructComplexSuccess-8 1000000 1431 ns/op 128 B/op 8 allocs/op -BenchmarkStructComplexSuccessParallel-8 3000000 427 ns/op 128 B/op 8 allocs/op -BenchmarkStructComplexFailure-8 300000 4065 ns/op 3041 B/op 53 allocs/op -BenchmarkStructComplexFailureParallel-8 1000000 1478 ns/op 3041 B/op 53 allocs/op +goos: darwin +goarch: amd64 +pkg: github.com/go-playground/validator +BenchmarkFieldSuccess-8 20000000 83.6 ns/op 0 B/op 0 allocs/op +BenchmarkFieldSuccessParallel-8 50000000 26.8 ns/op 0 B/op 0 allocs/op +BenchmarkFieldFailure-8 5000000 291 ns/op 208 B/op 4 allocs/op +BenchmarkFieldFailureParallel-8 20000000 107 ns/op 208 B/op 4 allocs/op +BenchmarkFieldArrayDiveSuccess-8 2000000 623 ns/op 201 B/op 11 allocs/op +BenchmarkFieldArrayDiveSuccessParallel-8 10000000 237 ns/op 201 B/op 11 allocs/op +BenchmarkFieldArrayDiveFailure-8 2000000 859 ns/op 412 B/op 16 allocs/op +BenchmarkFieldArrayDiveFailureParallel-8 5000000 335 ns/op 413 B/op 16 allocs/op +BenchmarkFieldMapDiveSuccess-8 1000000 1292 ns/op 432 B/op 18 allocs/op +BenchmarkFieldMapDiveSuccessParallel-8 3000000 467 ns/op 432 B/op 18 allocs/op +BenchmarkFieldMapDiveFailure-8 1000000 1082 ns/op 512 B/op 16 allocs/op +BenchmarkFieldMapDiveFailureParallel-8 5000000 425 ns/op 512 B/op 16 allocs/op +BenchmarkFieldMapDiveWithKeysSuccess-8 1000000 1539 ns/op 480 B/op 21 allocs/op +BenchmarkFieldMapDiveWithKeysSuccessParallel-8 3000000 613 ns/op 480 B/op 21 allocs/op +BenchmarkFieldMapDiveWithKeysFailure-8 1000000 1413 ns/op 721 B/op 21 allocs/op +BenchmarkFieldMapDiveWithKeysFailureParallel-8 3000000 575 ns/op 721 B/op 21 allocs/op +BenchmarkFieldCustomTypeSuccess-8 10000000 216 ns/op 32 B/op 2 allocs/op +BenchmarkFieldCustomTypeSuccessParallel-8 20000000 82.2 ns/op 32 B/op 2 allocs/op +BenchmarkFieldCustomTypeFailure-8 5000000 274 ns/op 208 B/op 4 allocs/op +BenchmarkFieldCustomTypeFailureParallel-8 20000000 116 ns/op 208 B/op 4 allocs/op +BenchmarkFieldOrTagSuccess-8 2000000 740 ns/op 16 B/op 1 allocs/op +BenchmarkFieldOrTagSuccessParallel-8 3000000 474 ns/op 16 B/op 1 allocs/op +BenchmarkFieldOrTagFailure-8 3000000 471 ns/op 224 B/op 5 allocs/op +BenchmarkFieldOrTagFailureParallel-8 3000000 414 ns/op 224 B/op 5 allocs/op +BenchmarkStructLevelValidationSuccess-8 10000000 213 ns/op 32 B/op 2 allocs/op +BenchmarkStructLevelValidationSuccessParallel-8 20000000 91.8 ns/op 32 B/op 2 allocs/op +BenchmarkStructLevelValidationFailure-8 3000000 473 ns/op 304 B/op 8 allocs/op +BenchmarkStructLevelValidationFailureParallel-8 10000000 234 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCustomTypeSuccess-8 5000000 385 ns/op 32 B/op 2 allocs/op +BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 161 ns/op 32 B/op 2 allocs/op +BenchmarkStructSimpleCustomTypeFailure-8 2000000 640 ns/op 424 B/op 9 allocs/op +BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 318 ns/op 440 B/op 10 allocs/op +BenchmarkStructFilteredSuccess-8 2000000 597 ns/op 288 B/op 9 allocs/op +BenchmarkStructFilteredSuccessParallel-8 10000000 266 ns/op 288 B/op 9 allocs/op +BenchmarkStructFilteredFailure-8 3000000 454 ns/op 256 B/op 7 allocs/op +BenchmarkStructFilteredFailureParallel-8 10000000 214 ns/op 256 B/op 7 allocs/op +BenchmarkStructPartialSuccess-8 3000000 502 ns/op 256 B/op 6 allocs/op +BenchmarkStructPartialSuccessParallel-8 10000000 225 ns/op 256 B/op 6 allocs/op +BenchmarkStructPartialFailure-8 2000000 702 ns/op 480 B/op 11 allocs/op +BenchmarkStructPartialFailureParallel-8 5000000 329 ns/op 480 B/op 11 allocs/op +BenchmarkStructExceptSuccess-8 2000000 793 ns/op 496 B/op 12 allocs/op +BenchmarkStructExceptSuccessParallel-8 10000000 193 ns/op 240 B/op 5 allocs/op +BenchmarkStructExceptFailure-8 2000000 639 ns/op 464 B/op 10 allocs/op +BenchmarkStructExceptFailureParallel-8 5000000 300 ns/op 464 B/op 10 allocs/op +BenchmarkStructSimpleCrossFieldSuccess-8 3000000 417 ns/op 72 B/op 3 allocs/op +BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 163 ns/op 72 B/op 3 allocs/op +BenchmarkStructSimpleCrossFieldFailure-8 2000000 645 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 285 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 3000000 588 ns/op 80 B/op 4 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 221 ns/op 80 B/op 4 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 868 ns/op 320 B/op 9 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 337 ns/op 320 B/op 9 allocs/op +BenchmarkStructSimpleSuccess-8 5000000 260 ns/op 0 B/op 0 allocs/op +BenchmarkStructSimpleSuccessParallel-8 20000000 90.6 ns/op 0 B/op 0 allocs/op +BenchmarkStructSimpleFailure-8 2000000 619 ns/op 424 B/op 9 allocs/op +BenchmarkStructSimpleFailureParallel-8 5000000 296 ns/op 424 B/op 9 allocs/op +BenchmarkStructComplexSuccess-8 1000000 1454 ns/op 128 B/op 8 allocs/op +BenchmarkStructComplexSuccessParallel-8 3000000 579 ns/op 128 B/op 8 allocs/op +BenchmarkStructComplexFailure-8 300000 4140 ns/op 3041 B/op 53 allocs/op +BenchmarkStructComplexFailureParallel-8 1000000 2127 ns/op 3041 B/op 53 allocs/op +BenchmarkOneof-8 10000000 140 ns/op 0 B/op 0 allocs/op +BenchmarkOneofParallel-8 20000000 70.1 ns/op 0 B/op 0 allocs/op ``` Complementary Software diff --git a/vendor/gopkg.in/go-playground/validator.v9/baked_in.go b/vendor/gopkg.in/go-playground/validator.v9/baked_in.go index 665409468..a589ecc5f 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/baked_in.go +++ b/vendor/gopkg.in/go-playground/validator.v9/baked_in.go @@ -1,14 +1,21 @@ package validator import ( + "bytes" "context" + "crypto/sha256" "fmt" "net" "net/url" + "os" "reflect" + "strconv" "strings" + "sync" "time" "unicode/utf8" + + urn "github.com/leodido/go-urn" ) // Func accepts a FieldLevel interface for all validation needs. The return @@ -55,89 +62,165 @@ var ( // you can add, remove or even replace items to suite your needs, // or even disregard and use your own map if so desired. bakedInValidators = map[string]Func{ - "required": hasValue, - "isdefault": isDefault, - "len": hasLengthOf, - "min": hasMinOf, - "max": hasMaxOf, - "eq": isEq, - "ne": isNe, - "lt": isLt, - "lte": isLte, - "gt": isGt, - "gte": isGte, - "eqfield": isEqField, - "eqcsfield": isEqCrossStructField, - "necsfield": isNeCrossStructField, - "gtcsfield": isGtCrossStructField, - "gtecsfield": isGteCrossStructField, - "ltcsfield": isLtCrossStructField, - "ltecsfield": isLteCrossStructField, - "nefield": isNeField, - "gtefield": isGteField, - "gtfield": isGtField, - "ltefield": isLteField, - "ltfield": isLtField, - "alpha": isAlpha, - "alphanum": isAlphanum, - "alphaunicode": isAlphaUnicode, - "alphanumunicode": isAlphanumUnicode, - "numeric": isNumeric, - "number": isNumber, - "hexadecimal": isHexadecimal, - "hexcolor": isHEXColor, - "rgb": isRGB, - "rgba": isRGBA, - "hsl": isHSL, - "hsla": isHSLA, - "email": isEmail, - "url": isURL, - "uri": isURI, - "base64": isBase64, - "contains": contains, - "containsany": containsAny, - "containsrune": containsRune, - "excludes": excludes, - "excludesall": excludesAll, - "excludesrune": excludesRune, - "isbn": isISBN, - "isbn10": isISBN10, - "isbn13": isISBN13, - "uuid": isUUID, - "uuid3": isUUID3, - "uuid4": isUUID4, - "uuid5": isUUID5, - "ascii": isASCII, - "printascii": isPrintableASCII, - "multibyte": hasMultiByteCharacter, - "datauri": isDataURI, - "latitude": isLatitude, - "longitude": isLongitude, - "ssn": isSSN, - "ipv4": isIPv4, - "ipv6": isIPv6, - "ip": isIP, - "cidrv4": isCIDRv4, - "cidrv6": isCIDRv6, - "cidr": isCIDR, - "tcp4_addr": isTCP4AddrResolvable, - "tcp6_addr": isTCP6AddrResolvable, - "tcp_addr": isTCPAddrResolvable, - "udp4_addr": isUDP4AddrResolvable, - "udp6_addr": isUDP6AddrResolvable, - "udp_addr": isUDPAddrResolvable, - "ip4_addr": isIP4AddrResolvable, - "ip6_addr": isIP6AddrResolvable, - "ip_addr": isIPAddrResolvable, - "unix_addr": isUnixAddrResolvable, - "mac": isMAC, - "hostname": isHostname, - "fqdn": isFQDN, - "unique": isUnique, + "required": hasValue, + "required_with": requiredWith, + "required_with_all": requiredWithAll, + "required_without": requiredWithout, + "required_without_all": requiredWithoutAll, + "isdefault": isDefault, + "len": hasLengthOf, + "min": hasMinOf, + "max": hasMaxOf, + "eq": isEq, + "ne": isNe, + "lt": isLt, + "lte": isLte, + "gt": isGt, + "gte": isGte, + "eqfield": isEqField, + "eqcsfield": isEqCrossStructField, + "necsfield": isNeCrossStructField, + "gtcsfield": isGtCrossStructField, + "gtecsfield": isGteCrossStructField, + "ltcsfield": isLtCrossStructField, + "ltecsfield": isLteCrossStructField, + "nefield": isNeField, + "gtefield": isGteField, + "gtfield": isGtField, + "ltefield": isLteField, + "ltfield": isLtField, + "fieldcontains": fieldContains, + "fieldexcludes": fieldExcludes, + "alpha": isAlpha, + "alphanum": isAlphanum, + "alphaunicode": isAlphaUnicode, + "alphanumunicode": isAlphanumUnicode, + "numeric": isNumeric, + "number": isNumber, + "hexadecimal": isHexadecimal, + "hexcolor": isHEXColor, + "rgb": isRGB, + "rgba": isRGBA, + "hsl": isHSL, + "hsla": isHSLA, + "email": isEmail, + "url": isURL, + "uri": isURI, + "urn_rfc2141": isUrnRFC2141, // RFC 2141 + "file": isFile, + "base64": isBase64, + "base64url": isBase64URL, + "contains": contains, + "containsany": containsAny, + "containsrune": containsRune, + "excludes": excludes, + "excludesall": excludesAll, + "excludesrune": excludesRune, + "startswith": startsWith, + "endswith": endsWith, + "isbn": isISBN, + "isbn10": isISBN10, + "isbn13": isISBN13, + "eth_addr": isEthereumAddress, + "btc_addr": isBitcoinAddress, + "btc_addr_bech32": isBitcoinBech32Address, + "uuid": isUUID, + "uuid3": isUUID3, + "uuid4": isUUID4, + "uuid5": isUUID5, + "uuid_rfc4122": isUUIDRFC4122, + "uuid3_rfc4122": isUUID3RFC4122, + "uuid4_rfc4122": isUUID4RFC4122, + "uuid5_rfc4122": isUUID5RFC4122, + "ascii": isASCII, + "printascii": isPrintableASCII, + "multibyte": hasMultiByteCharacter, + "datauri": isDataURI, + "latitude": isLatitude, + "longitude": isLongitude, + "ssn": isSSN, + "ipv4": isIPv4, + "ipv6": isIPv6, + "ip": isIP, + "cidrv4": isCIDRv4, + "cidrv6": isCIDRv6, + "cidr": isCIDR, + "tcp4_addr": isTCP4AddrResolvable, + "tcp6_addr": isTCP6AddrResolvable, + "tcp_addr": isTCPAddrResolvable, + "udp4_addr": isUDP4AddrResolvable, + "udp6_addr": isUDP6AddrResolvable, + "udp_addr": isUDPAddrResolvable, + "ip4_addr": isIP4AddrResolvable, + "ip6_addr": isIP6AddrResolvable, + "ip_addr": isIPAddrResolvable, + "unix_addr": isUnixAddrResolvable, + "mac": isMAC, + "hostname": isHostnameRFC952, // RFC 952 + "hostname_rfc1123": isHostnameRFC1123, // RFC 1123 + "fqdn": isFQDN, + "unique": isUnique, + "oneof": isOneOf, + "html": isHTML, + "html_encoded": isHTMLEncoded, + "url_encoded": isURLEncoded, + "dir": isDir, } ) -// isUnique is the validation function for validating if each array|slice element is unique +var oneofValsCache = map[string][]string{} +var oneofValsCacheRWLock = sync.RWMutex{} + +func parseOneOfParam2(s string) []string { + oneofValsCacheRWLock.RLock() + vals, ok := oneofValsCache[s] + oneofValsCacheRWLock.RUnlock() + if !ok { + oneofValsCacheRWLock.Lock() + vals = strings.Fields(s) + oneofValsCache[s] = vals + oneofValsCacheRWLock.Unlock() + } + return vals +} + +func isURLEncoded(fl FieldLevel) bool { + return uRLEncodedRegex.MatchString(fl.Field().String()) +} + +func isHTMLEncoded(fl FieldLevel) bool { + return hTMLEncodedRegex.MatchString(fl.Field().String()) +} + +func isHTML(fl FieldLevel) bool { + return hTMLRegex.MatchString(fl.Field().String()) +} + +func isOneOf(fl FieldLevel) bool { + vals := parseOneOfParam2(fl.Param()) + + field := fl.Field() + + var v string + switch field.Kind() { + case reflect.String: + v = field.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + v = strconv.FormatInt(field.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + v = strconv.FormatUint(field.Uint(), 10) + default: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) + } + for i := 0; i < len(vals); i++ { + if vals[i] == v { + return true + } + } + return false +} + +// isUnique is the validation function for validating if each array|slice|map value is unique func isUnique(fl FieldLevel) bool { field := fl.Field() @@ -145,12 +228,19 @@ func isUnique(fl FieldLevel) bool { switch field.Kind() { case reflect.Slice, reflect.Array: - m := reflect.MakeMap(reflect.MapOf(fl.Field().Type().Elem(), v.Type())) + m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) for i := 0; i < field.Len(); i++ { m.SetMapIndex(field.Index(i), v) } return field.Len() == m.Len() + case reflect.Map: + m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) + + for _, k := range field.MapKeys() { + m.SetMapIndex(field.MapIndex(k), v) + } + return field.Len() == m.Len() default: panic(fmt.Sprintf("Bad field type %T", field.Interface())) } @@ -226,12 +316,48 @@ func isSSN(fl FieldLevel) bool { // IsLongitude is the validation function for validating if the field's value is a valid longitude coordinate. func isLongitude(fl FieldLevel) bool { - return longitudeRegex.MatchString(fl.Field().String()) + field := fl.Field() + + var v string + switch field.Kind() { + case reflect.String: + v = field.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + v = strconv.FormatInt(field.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + v = strconv.FormatUint(field.Uint(), 10) + case reflect.Float32: + v = strconv.FormatFloat(field.Float(), 'f', -1, 32) + case reflect.Float64: + v = strconv.FormatFloat(field.Float(), 'f', -1, 64) + default: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) + } + + return longitudeRegex.MatchString(v) } // IsLatitude is the validation function for validating if the field's value is a valid latitude coordinate. func isLatitude(fl FieldLevel) bool { - return latitudeRegex.MatchString(fl.Field().String()) + field := fl.Field() + + var v string + switch field.Kind() { + case reflect.String: + v = field.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + v = strconv.FormatInt(field.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + v = strconv.FormatUint(field.Uint(), 10) + case reflect.Float32: + v = strconv.FormatFloat(field.Float(), 'f', -1, 32) + case reflect.Float64: + v = strconv.FormatFloat(field.Float(), 'f', -1, 64) + default: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) + } + + return latitudeRegex.MatchString(v) } // IsDataURI is the validation function for validating if the field's value is a valid data URI. @@ -292,6 +418,26 @@ func isUUID(fl FieldLevel) bool { return uUIDRegex.MatchString(fl.Field().String()) } +// IsUUID5RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v5 UUID. +func isUUID5RFC4122(fl FieldLevel) bool { + return uUID5RFC4122Regex.MatchString(fl.Field().String()) +} + +// IsUUID4RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v4 UUID. +func isUUID4RFC4122(fl FieldLevel) bool { + return uUID4RFC4122Regex.MatchString(fl.Field().String()) +} + +// IsUUID3RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v3 UUID. +func isUUID3RFC4122(fl FieldLevel) bool { + return uUID3RFC4122Regex.MatchString(fl.Field().String()) +} + +// IsUUIDRFC4122 is the validation function for validating if the field's value is a valid RFC4122 UUID of any version. +func isUUIDRFC4122(fl FieldLevel) bool { + return uUIDRFC4122Regex.MatchString(fl.Field().String()) +} + // IsISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN. func isISBN(fl FieldLevel) bool { return isISBN10(fl) || isISBN13(fl) @@ -343,6 +489,140 @@ func isISBN10(fl FieldLevel) bool { return checksum%11 == 0 } +// IsEthereumAddress is the validation function for validating if the field's value is a valid ethereum address based currently only on the format +func isEthereumAddress(fl FieldLevel) bool { + address := fl.Field().String() + + if !ethAddressRegex.MatchString(address) { + return false + } + + if ethaddressRegexUpper.MatchString(address) || ethAddressRegexLower.MatchString(address) { + return true + } + + // checksum validation is blocked by https://github.com/golang/crypto/pull/28 + + return true +} + +// IsBitcoinAddress is the validation function for validating if the field's value is a valid btc address +func isBitcoinAddress(fl FieldLevel) bool { + address := fl.Field().String() + + if !btcAddressRegex.MatchString(address) { + return false + } + + alphabet := []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") + + decode := [25]byte{} + + for _, n := range []byte(address) { + d := bytes.IndexByte(alphabet, n) + + for i := 24; i >= 0; i-- { + d += 58 * int(decode[i]) + decode[i] = byte(d % 256) + d /= 256 + } + } + + h := sha256.New() + _, _ = h.Write(decode[:21]) + d := h.Sum([]byte{}) + h = sha256.New() + _, _ = h.Write(d) + + validchecksum := [4]byte{} + computedchecksum := [4]byte{} + + copy(computedchecksum[:], h.Sum(d[:0])) + copy(validchecksum[:], decode[21:]) + + return validchecksum == computedchecksum +} + +// IsBitcoinBech32Address is the validation function for validating if the field's value is a valid bech32 btc address +func isBitcoinBech32Address(fl FieldLevel) bool { + address := fl.Field().String() + + if !btcLowerAddressRegexBech32.MatchString(address) && !btcUpperAddressRegexBech32.MatchString(address) { + return false + } + + am := len(address) % 8 + + if am == 0 || am == 3 || am == 5 { + return false + } + + address = strings.ToLower(address) + + alphabet := "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + + hr := []int{3, 3, 0, 2, 3} // the human readable part will always be bc + addr := address[3:] + dp := make([]int, 0, len(addr)) + + for _, c := range addr { + dp = append(dp, strings.IndexRune(alphabet, c)) + } + + ver := dp[0] + + if ver < 0 || ver > 16 { + return false + } + + if ver == 0 { + if len(address) != 42 && len(address) != 62 { + return false + } + } + + values := append(hr, dp...) + + GEN := []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} + + p := 1 + + for _, v := range values { + b := p >> 25 + p = (p&0x1ffffff)<<5 ^ v + + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + p ^= GEN[i] + } + } + } + + if p != 1 { + return false + } + + b := uint(0) + acc := 0 + mv := (1 << 5) - 1 + var sw []int + + for _, v := range dp[1 : len(dp)-6] { + acc = (acc << 5) | v + b += 5 + for b >= 8 { + b -= 8 + sw = append(sw, (acc>>b)&mv) + } + } + + if len(sw) < 2 || len(sw) > 40 { + return false + } + + return true +} + // ExcludesRune is the validation function for validating that the field's value does not contain the rune specified within the param. func excludesRune(fl FieldLevel) bool { return !containsRune(fl) @@ -376,6 +656,41 @@ func contains(fl FieldLevel) bool { return strings.Contains(fl.Field().String(), fl.Param()) } +// StartsWith is the validation function for validating that the field's value starts with the text specified within the param. +func startsWith(fl FieldLevel) bool { + return strings.HasPrefix(fl.Field().String(), fl.Param()) +} + +// EndsWith is the validation function for validating that the field's value ends with the text specified within the param. +func endsWith(fl FieldLevel) bool { + return strings.HasSuffix(fl.Field().String(), fl.Param()) +} + +// FieldContains is the validation function for validating if the current field's value contains the field specified by the param's value. +func fieldContains(fl FieldLevel) bool { + field := fl.Field() + + currentField, _, ok := fl.GetStructFieldOK() + + if !ok { + return false + } + + return strings.Contains(field.String(), currentField.String()) +} + +// FieldExcludes is the validation function for validating if the current field's value excludes the field specified by the param's value. +func fieldExcludes(fl FieldLevel) bool { + field := fl.Field() + + currentField, _, ok := fl.GetStructFieldOK() + if !ok { + return true + } + + return !strings.Contains(field.String(), currentField.String()) +} + // IsNeField is the validation function for validating if the current field's value is not equal to the field specified by the param's value. func isNeField(fl FieldLevel) bool { @@ -801,6 +1116,11 @@ func isBase64(fl FieldLevel) bool { return base64Regex.MatchString(fl.Field().String()) } +// IsBase64URL is the validation function for validating if the current field's value is a valid base64 URL safe string. +func isBase64URL(fl FieldLevel) bool { + return base64URLRegex.MatchString(fl.Field().String()) +} + // IsURI is the validation function for validating if the current field's value is a valid URI. func isURI(fl FieldLevel) bool { @@ -864,6 +1184,41 @@ func isURL(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } +// isUrnRFC2141 is the validation function for validating if the current field's value is a valid URN as per RFC 2141. +func isUrnRFC2141(fl FieldLevel) bool { + field := fl.Field() + + switch field.Kind() { + + case reflect.String: + + str := field.String() + + _, match := urn.Parse([]byte(str)) + + return match + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsFile is the validation function for validating if the current field's value is a valid file path. +func isFile(fl FieldLevel) bool { + field := fl.Field() + + switch field.Kind() { + case reflect.String: + fileInfo, err := os.Stat(field.String()) + if err != nil { + return false + } + + return !fileInfo.IsDir() + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + // IsEmail is the validation function for validating if the current field's value is a valid email address. func isEmail(fl FieldLevel) bool { return emailRegex.MatchString(fl.Field().String()) @@ -901,12 +1256,22 @@ func isHexadecimal(fl FieldLevel) bool { // IsNumber is the validation function for validating if the current field's value is a valid number. func isNumber(fl FieldLevel) bool { - return numberRegex.MatchString(fl.Field().String()) + switch fl.Field().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64: + return true + default: + return numberRegex.MatchString(fl.Field().String()) + } } // IsNumeric is the validation function for validating if the current field's value is a valid numeric value. func isNumeric(fl FieldLevel) bool { - return numericRegex.MatchString(fl.Field().String()) + switch fl.Field().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64: + return true + default: + return numericRegex.MatchString(fl.Field().String()) + } } // IsAlphanum is the validation function for validating if the current field's value is a valid alphanumeric value. @@ -936,22 +1301,101 @@ func isDefault(fl FieldLevel) bool { // HasValue is the validation function for validating if the current field's value is not the default static value. func hasValue(fl FieldLevel) bool { + return requireCheckFieldKind(fl, "") +} +// requireCheckField is a func for check field kind +func requireCheckFieldKind(fl FieldLevel, param string) bool { field := fl.Field() - + if len(param) > 0 { + field = fl.Parent().FieldByName(param) + } switch field.Kind() { case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: return !field.IsNil() default: - if fl.(*validate).fldIsPointer && field.Interface() != nil { return true } - return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface() } } +// RequiredWith is the validation function +// The field under validation must be present and not empty only if any of the other specified fields are present. +func requiredWith(fl FieldLevel) bool { + + params := parseOneOfParam2(fl.Param()) + for _, param := range params { + + if requireCheckFieldKind(fl, param) { + return requireCheckFieldKind(fl, "") + } + } + + return true +} + +// RequiredWithAll is the validation function +// The field under validation must be present and not empty only if all of the other specified fields are present. +func requiredWithAll(fl FieldLevel) bool { + + isValidateCurrentField := true + params := parseOneOfParam2(fl.Param()) + for _, param := range params { + + if !requireCheckFieldKind(fl, param) { + isValidateCurrentField = false + } + } + + if isValidateCurrentField { + return requireCheckFieldKind(fl, "") + } + + return true +} + +// RequiredWithout is the validation function +// The field under validation must be present and not empty only when any of the other specified fields are not present. +func requiredWithout(fl FieldLevel) bool { + + isValidateCurrentField := false + params := parseOneOfParam2(fl.Param()) + for _, param := range params { + + if requireCheckFieldKind(fl, param) { + isValidateCurrentField = true + } + } + + if !isValidateCurrentField { + return requireCheckFieldKind(fl, "") + } + + return true +} + +// RequiredWithoutAll is the validation function +// The field under validation must be present and not empty only when all of the other specified fields are not present. +func requiredWithoutAll(fl FieldLevel) bool { + + isValidateCurrentField := true + params := parseOneOfParam2(fl.Param()) + for _, param := range params { + + if requireCheckFieldKind(fl, param) { + isValidateCurrentField = false + } + } + + if isValidateCurrentField { + return requireCheckFieldKind(fl, "") + } + + return true +} + // IsGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value. func isGteField(fl FieldLevel) bool { @@ -1511,8 +1955,12 @@ func isIP6Addr(fl FieldLevel) bool { return ip != nil && ip.To4() == nil } -func isHostname(fl FieldLevel) bool { - return hostnameRegex.MatchString(fl.Field().String()) +func isHostnameRFC952(fl FieldLevel) bool { + return hostnameRegexRFC952.MatchString(fl.Field().String()) +} + +func isHostnameRFC1123(fl FieldLevel) bool { + return hostnameRegexRFC1123.MatchString(fl.Field().String()) } func isFQDN(fl FieldLevel) bool { @@ -1526,6 +1974,22 @@ func isFQDN(fl FieldLevel) bool { val = val[0 : len(val)-1] } - return (strings.IndexAny(val, ".") > -1) && - hostnameRegex.MatchString(val) + return strings.ContainsAny(val, ".") && + hostnameRegexRFC952.MatchString(val) +} + +// IsDir is the validation function for validating if the current field's value is a valid directory. +func isDir(fl FieldLevel) bool { + field := fl.Field() + + if field.Kind() == reflect.String { + fileInfo, err := os.Stat(field.String()) + if err != nil { + return false + } + + return fileInfo.IsDir() + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) } diff --git a/vendor/gopkg.in/go-playground/validator.v9/cache.go b/vendor/gopkg.in/go-playground/validator.v9/cache.go index 3906d2517..a7a4202f6 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/cache.go +++ b/vendor/gopkg.in/go-playground/validator.v9/cache.go @@ -91,14 +91,14 @@ type cTag struct { aliasTag string actualAliasTag string param string - typeof tagType keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation next *cTag + fn FuncCtx + typeof tagType hasTag bool hasAlias bool hasParam bool // true if parameter used eg. eq= where the equal sign has been set isBlockEnd bool // indicates the current tag represents the last validation in the block - fn FuncCtx } func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct { @@ -223,7 +223,7 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s current.typeof = typeKeys if i == 0 || prevTag != typeDive { - panic(fmt.Sprintf("'%s' tag must be immediately preceeded by the '%s' tag", keysTag, diveTag)) + panic(fmt.Sprintf("'%s' tag must be immediately preceded by the '%s' tag", keysTag, diveTag)) } current.typeof = typeKeys diff --git a/vendor/gopkg.in/go-playground/validator.v9/doc.go b/vendor/gopkg.in/go-playground/validator.v9/doc.go index d7efa1995..e0396cb44 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/doc.go +++ b/vendor/gopkg.in/go-playground/validator.v9/doc.go @@ -31,7 +31,7 @@ Custom Validation Functions Custom Validation functions can be added. Example: // Structure - func customFunc(fl FieldLevel) bool { + func customFunc(fl validator.FieldLevel) bool { if fl.Field().String() == "invalid" { return false @@ -94,7 +94,7 @@ used "eqcsfield" it could be multiple levels down. Example: // NOTE: when calling validate.Struct(val) topStruct will be the top level struct passed // into the function - // when calling validate.FieldWithValue(val, field, tag) val will be + // when calling validate.VarWithValue(val, field, tag) val will be // whatever you pass, struct, field... // when calling validate.Field(field, tag) val will be nil @@ -168,7 +168,7 @@ StructOnly When a field that is a nested struct is encountered, and contains this flag any validation on the nested struct will be run, but none of the nested -struct fields will be validated. This is usefull if inside of you program +struct fields will be validated. This is useful if inside of your program you know the struct will be valid, but need to verify it has been assigned. NOTE: only "required" and "omitempty" can be used on a struct itself. @@ -245,6 +245,68 @@ ensures the value is not nil. Usage: required +Required With + +The field under validation must be present and not empty only if any +of the other specified fields are present. For strings ensures value is +not "". For slices, maps, pointers, interfaces, channels and functions +ensures the value is not nil. + + Usage: required_with + +Examples: + + // require the field if the Field1 is present: + Usage: required_with=Field1 + + // require the field if the Field1 or Field2 is present: + Usage: required_with=Field1 Field2 + +Required With All + +The field under validation must be present and not empty only if all +of the other specified fields are present. For strings ensures value is +not "". For slices, maps, pointers, interfaces, channels and functions +ensures the value is not nil. + + Usage: required_with_all + +Example: + + // require the field if the Field1 and Field2 is present: + Usage: required_with_all=Field1 Field2 + +Required Without + +The field under validation must be present and not empty only when any +of the other specified fields are not present. For strings ensures value is +not "". For slices, maps, pointers, interfaces, channels and functions +ensures the value is not nil. + + Usage: required_without + +Examples: + + // require the field if the Field1 is not present: + Usage: required_without=Field1 + + // require the field if the Field1 or Field2 is not present: + Usage: required_without=Field1 Field2 + +Required Without All + +The field under validation must be present and not empty only when all +of the other specified fields are not present. For strings ensures value is +not "". For slices, maps, pointers, interfaces, channels and functions +ensures the value is not nil. + + Usage: required_without_all + +Example: + + // require the field if the Field1 and Field2 is not present: + Usage: required_without_all=Field1 Field2 + Is Default This validates that the value is the default value and is almost the @@ -295,6 +357,16 @@ validates the number of items. Usage: ne=10 +One Of + +For strings, ints, and uints, oneof will ensure that the value +is one of the values in the parameter. The parameter should be +a list of values separated by whitespace. Values may be +strings or numbers. + + Usage: oneof=red green + oneof=5 7 9 + Greater Than For numbers, this will ensure that the value is greater than the @@ -369,7 +441,7 @@ Example #1: Example #2: // Validating by field: - validate.FieldWithValue(password, confirmpassword, "eqfield") + validate.VarWithValue(password, confirmpassword, "eqfield") Field Equals Another Field (relative) @@ -391,7 +463,7 @@ Examples: Usage: nefield=Color2 // Validating by field: - validate.FieldWithValue(color1, color2, "nefield") + validate.VarWithValue(color1, color2, "nefield") Field Does Not Equal Another Field (relative) @@ -414,7 +486,7 @@ Example #1: Example #2: // Validating by field: - validate.FieldWithValue(start, end, "gtfield") + validate.VarWithValue(start, end, "gtfield") Field Greater Than Another Relative Field @@ -438,7 +510,7 @@ Example #1: Example #2: // Validating by field: - validate.FieldWithValue(start, end, "gtefield") + validate.VarWithValue(start, end, "gtefield") Field Greater Than or Equal To Another Relative Field @@ -461,7 +533,7 @@ Example #1: Example #2: // Validating by field: - validate.FieldWithValue(start, end, "ltfield") + validate.VarWithValue(start, end, "ltfield") Less Than Another Relative Field @@ -484,7 +556,7 @@ Example #1: Example #2: // Validating by field: - validate.FieldWithValue(start, end, "ltefield") + validate.VarWithValue(start, end, "ltefield") Less Than or Equal To Another Relative Field @@ -493,9 +565,26 @@ to the top level struct. Usage: ltecsfield=InnerStructField.Field +Field Contains Another Field + +This does the same as contains except for struct fields. It should only be used +with string types. See the behavior of reflect.Value.String() for behavior on +other types. + + Usage: containsfield=InnerStructField.Field + +Field Excludes Another Field + +This does the same as excludes except for struct fields. It should only be used +with string types. See the behavior of reflect.Value.String() for behavior on +other types. + + Usage: excludesfield=InnerStructField.Field + Unique For arrays & slices, unique will ensure that there are no duplicates. +For maps, unique will ensure that there are no duplicate values. Usage: unique @@ -527,6 +616,7 @@ Numeric This validates that a string value contains a basic numeric value. basic excludes exponents etc... +for integers or float it returns true. Usage: numeric @@ -571,10 +661,18 @@ E-mail String This validates that a string value contains a valid email This may not conform to all possibilities of any rfc standard, but neither -does any email provider accept all posibilities. +does any email provider accept all possibilities. Usage: email +File path + +This validates that a string value contains a valid file path and that +the file exists on the machine. +This is done using os.Stat, which is a platform independent function. + + Usage: file + URL String This validates that a string value contains a valid url @@ -590,6 +688,13 @@ This will accept any uri the golang request uri accepts Usage: uri +Urn RFC 2141 String + +This validataes that a string value contains a valid URN +according to the RFC 2141 spec. + + Usage: urn_rfc2141 + Base64 String This validates that a string value contains a valid base64 value. @@ -599,6 +704,40 @@ this with the omitempty tag. Usage: base64 +Base64URL String + +This validates that a string value contains a valid base64 URL safe value +according the the RFC4648 spec. +Although an empty string is a valid base64 URL safe value, this will report +an empty string as an error, if you wish to accept an empty string as valid +you can use this with the omitempty tag. + + Usage: base64url + +Bitcoin Address + +This validates that a string value contains a valid bitcoin address. +The format of the string is checked to ensure it matches one of the three formats +P2PKH, P2SH and performs checksum validation. + + Usage: btc_addr + +Bitcoin Bech32 Address (segwit) + +This validates that a string value contains a valid bitcoin Bech32 address as defined +by bip-0173 (https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) +Special thanks to Pieter Wuille for providng reference implementations. + + Usage: btc_addr_bech32 + +Ethereum Address + +This validates that a string value contains a valid ethereum address. +The format of the string is checked to ensure it matches the standard Ethereum address format +Full validation is blocked by https://github.com/golang/crypto/pull/28 + + Usage: eth_addr + Contains This validates that a string value contains the substring value. @@ -637,6 +776,18 @@ This validates that a string value does not contain the supplied rune value. Usage: excludesrune=@ +Starts With + +This validates that a string value starts with the supplied string value + + Usage: startswith=hello + +Ends With + +This validates that a string value ends with the supplied string value + + Usage: endswith=goodbye + International Standard Book Number This validates that a string value contains a valid isbn10 or isbn13 value. @@ -655,28 +806,27 @@ This validates that a string value contains a valid isbn13 value. Usage: isbn13 - Universally Unique Identifier UUID -This validates that a string value contains a valid UUID. +This validates that a string value contains a valid UUID. Uppercase UUID values will not pass - use `uuid_rfc4122` instead. Usage: uuid Universally Unique Identifier UUID v3 -This validates that a string value contains a valid version 3 UUID. +This validates that a string value contains a valid version 3 UUID. Uppercase UUID values will not pass - use `uuid3_rfc4122` instead. Usage: uuid3 Universally Unique Identifier UUID v4 -This validates that a string value contains a valid version 4 UUID. +This validates that a string value contains a valid version 4 UUID. Uppercase UUID values will not pass - use `uuid4_rfc4122` instead. Usage: uuid4 Universally Unique Identifier UUID v5 -This validates that a string value contains a valid version 5 UUID. +This validates that a string value contains a valid version 5 UUID. Uppercase UUID values will not pass - use `uuid5_rfc4122` instead. Usage: uuid5 @@ -728,103 +878,103 @@ This validates that a string value contains a valid U.S. Social Security Number. Internet Protocol Address IP -This validates that a string value contains a valid IP Adress. +This validates that a string value contains a valid IP Address. Usage: ip Internet Protocol Address IPv4 -This validates that a string value contains a valid v4 IP Adress. +This validates that a string value contains a valid v4 IP Address. Usage: ipv4 Internet Protocol Address IPv6 -This validates that a string value contains a valid v6 IP Adress. +This validates that a string value contains a valid v6 IP Address. Usage: ipv6 Classless Inter-Domain Routing CIDR -This validates that a string value contains a valid CIDR Adress. +This validates that a string value contains a valid CIDR Address. Usage: cidr Classless Inter-Domain Routing CIDRv4 -This validates that a string value contains a valid v4 CIDR Adress. +This validates that a string value contains a valid v4 CIDR Address. Usage: cidrv4 Classless Inter-Domain Routing CIDRv6 -This validates that a string value contains a valid v6 CIDR Adress. +This validates that a string value contains a valid v6 CIDR Address. Usage: cidrv6 Transmission Control Protocol Address TCP -This validates that a string value contains a valid resolvable TCP Adress. +This validates that a string value contains a valid resolvable TCP Address. Usage: tcp_addr Transmission Control Protocol Address TCPv4 -This validates that a string value contains a valid resolvable v4 TCP Adress. +This validates that a string value contains a valid resolvable v4 TCP Address. Usage: tcp4_addr Transmission Control Protocol Address TCPv6 -This validates that a string value contains a valid resolvable v6 TCP Adress. +This validates that a string value contains a valid resolvable v6 TCP Address. Usage: tcp6_addr User Datagram Protocol Address UDP -This validates that a string value contains a valid resolvable UDP Adress. +This validates that a string value contains a valid resolvable UDP Address. Usage: udp_addr User Datagram Protocol Address UDPv4 -This validates that a string value contains a valid resolvable v4 UDP Adress. +This validates that a string value contains a valid resolvable v4 UDP Address. Usage: udp4_addr User Datagram Protocol Address UDPv6 -This validates that a string value contains a valid resolvable v6 UDP Adress. +This validates that a string value contains a valid resolvable v6 UDP Address. Usage: udp6_addr Internet Protocol Address IP -This validates that a string value contains a valid resolvable IP Adress. +This validates that a string value contains a valid resolvable IP Address. Usage: ip_addr Internet Protocol Address IPv4 -This validates that a string value contains a valid resolvable v4 IP Adress. +This validates that a string value contains a valid resolvable v4 IP Address. Usage: ip4_addr Internet Protocol Address IPv6 -This validates that a string value contains a valid resolvable v6 IP Adress. +This validates that a string value contains a valid resolvable v6 IP Address. Usage: ip6_addr Unix domain socket end point Address -This validates that a string value contains a valid Unix Adress. +This validates that a string value contains a valid Unix Address. Usage: unix_addr Media Access Control Address MAC -This validates that a string value contains a valid MAC Adress. +This validates that a string value contains a valid MAC Address. Usage: mac @@ -832,18 +982,53 @@ Note: See Go's ParseMAC for accepted formats and types: http://golang.org/src/net/mac.go?s=866:918#L29 -Hostname +Hostname RFC 952 -This validates that a string value is a valid Hostname +This validates that a string value is a valid Hostname according to RFC 952 https://tools.ietf.org/html/rfc952 Usage: hostname +Hostname RFC 1123 + +This validates that a string value is a valid Hostname according to RFC 1123 https://tools.ietf.org/html/rfc1123 + + Usage: hostname_rfc1123 or if you want to continue to use 'hostname' in your tags, create an alias. + Full Qualified Domain Name (FQDN) This validates that a string value contains a valid FQDN. Usage: fqdn +HTML Tags + +This validates that a string value appears to be an HTML element tag +including those described at https://developer.mozilla.org/en-US/docs/Web/HTML/Element + + Usage: html + +HTML Encoded + +This validates that a string value is a proper character reference in decimal +or hexadecimal format + + Usage: html_encoded + +URL Encoded + +This validates that a string value is percent-encoded (URL encoded) according +to https://tools.ietf.org/html/rfc3986#section-2.1 + + Usage: url_encoded + +Directory + +This validates that a string value contains a valid directory and that +it exists on the machine. +This is done using os.Stat, which is a platform independent function. + + Usage: dir + Alias Validators and Tags NOTE: When returning an error, the tag returned in "FieldError" will be @@ -863,7 +1048,7 @@ Validator notes: of a regex which conflict with the validation definitions. Although workarounds can be made, they take away from using pure regex's. Furthermore it's quick and dirty but the regex's become harder to - maintain and are not reusable, so it's as much a programming philosiphy + maintain and are not reusable, so it's as much a programming philosophy as anything. In place of this new validator functions should be created; a regex can @@ -887,5 +1072,30 @@ that should not make it to production. } validate.Struct(t) // this will panic + +Non standard validators + +A collection of validation rules that are frequently needed but are more +complex than the ones found in the baked in validators. +A non standard validator must be registered manually using any tag you like. +See below examples of registration and use. + + type Test struct { + TestField string `validate:"yourtag"` + } + + t := &Test{ + TestField: "Test" + } + + validate := validator.New() + validate.RegisterValidation("yourtag", validations.ValidatorName) + + NotBlank + This validates that the value is not blank or with length zero. + For strings ensures they do not contain only spaces. For channels, maps, slices and arrays + ensures they don't have zero length. For others, a non empty value is required. + + Usage: notblank */ package validator diff --git a/vendor/gopkg.in/go-playground/validator.v9/errors.go b/vendor/gopkg.in/go-playground/validator.v9/errors.go index 85f65eff1..7dc0a358f 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/errors.go +++ b/vendor/gopkg.in/go-playground/validator.v9/errors.go @@ -122,7 +122,7 @@ type FieldError interface { // fields actual name. // // eq. JSON name "fname" - // see ActualField for comparison + // see StructField for comparison Field() string // returns the fields actual name from the struct, when able to determine. diff --git a/vendor/gopkg.in/go-playground/validator.v9/field_level.go b/vendor/gopkg.in/go-playground/validator.v9/field_level.go index 6d7319257..cbfbc1586 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/field_level.go +++ b/vendor/gopkg.in/go-playground/validator.v9/field_level.go @@ -17,7 +17,7 @@ type FieldLevel interface { Field() reflect.Value // returns the field's name with the tag - // name takeing precedence over the fields actual name. + // name taking precedence over the fields actual name. FieldName() string // returns the struct field's name diff --git a/vendor/gopkg.in/go-playground/validator.v9/regexes.go b/vendor/gopkg.in/go-playground/validator.v9/regexes.go index ec78cebc5..0253d7091 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/regexes.go +++ b/vendor/gopkg.in/go-playground/validator.v9/regexes.go @@ -3,63 +3,93 @@ package validator import "regexp" const ( - alphaRegexString = "^[a-zA-Z]+$" - alphaNumericRegexString = "^[a-zA-Z0-9]+$" - alphaUnicodeRegexString = "^[\\p{L}]+$" - alphaUnicodeNumericRegexString = "^[\\p{L}\\p{N}]+$" - numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$" - numberRegexString = "^[0-9]+$" - hexadecimalRegexString = "^[0-9a-fA-F]+$" - hexcolorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" - rgbRegexString = "^rgb\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*\\)$" - rgbaRegexString = "^rgba\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" - hslRegexString = "^hsl\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*\\)$" - hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" - emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:\\(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22)))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" - base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" - iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$" - iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$" - uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$" - uUID4RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" - uUID5RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" - uUIDRegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" - aSCIIRegexString = "^[\x00-\x7F]*$" - printableASCIIRegexString = "^[\x20-\x7E]*$" - multibyteRegexString = "[^\x00-\x7F]" - dataURIRegexString = "^data:.+\\/(.+);base64$" - latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" - longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" - sSNRegexString = `^\d{3}[- ]?\d{2}[- ]?\d{4}$` - hostnameRegexString = `^[a-zA-Z][a-zA-Z0-9\-\.]+[a-z-Az0-9]$` + alphaRegexString = "^[a-zA-Z]+$" + alphaNumericRegexString = "^[a-zA-Z0-9]+$" + alphaUnicodeRegexString = "^[\\p{L}]+$" + alphaUnicodeNumericRegexString = "^[\\p{L}\\p{N}]+$" + numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$" + numberRegexString = "^[0-9]+$" + hexadecimalRegexString = "^[0-9a-fA-F]+$" + hexcolorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + rgbRegexString = "^rgb\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*\\)$" + rgbaRegexString = "^rgba\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" + hslRegexString = "^hsl\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*\\)$" + hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" + emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" + base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" + base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$" + iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$" + iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$" + uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$" + uUID4RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + uUID5RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + uUIDRegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" + uUID3RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-3[0-9a-fA-F]{3}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" + uUID4RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + uUID5RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-5[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" + uUIDRFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" + aSCIIRegexString = "^[\x00-\x7F]*$" + printableASCIIRegexString = "^[\x20-\x7E]*$" + multibyteRegexString = "[^\x00-\x7F]" + dataURIRegexString = "^data:.+\\/(.+);base64$" + latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" + longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" + sSNRegexString = `^[0-9]{3}[ -]?(0[1-9]|[1-9][0-9])[ -]?([1-9][0-9]{3}|[0-9][1-9][0-9]{2}|[0-9]{2}[1-9][0-9]|[0-9]{3}[1-9])$` + hostnameRegexStringRFC952 = `^[a-zA-Z][a-zA-Z0-9\-\.]+[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952 + hostnameRegexStringRFC1123 = `^[a-zA-Z0-9][a-zA-Z0-9\-\.]+[a-zA-Z0-9]$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123 + btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address + btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32 + btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32 + ethAddressRegexString = `^0x[0-9a-fA-F]{40}$` + ethAddressUpperRegexString = `^0x[0-9A-F]{40}$` + ethAddressLowerRegexString = `^0x[0-9a-f]{40}$` + uRLEncodedRegexString = `(%[A-Fa-f0-9]{2})` + hTMLEncodedRegexString = `&#[x]?([0-9a-fA-F]{2})|(>)|(<)|(")|(&)+[;]?` + hTMLRegexString = `<[/]?([a-zA-Z]+).*?>` ) var ( - alphaRegex = regexp.MustCompile(alphaRegexString) - alphaNumericRegex = regexp.MustCompile(alphaNumericRegexString) - alphaUnicodeRegex = regexp.MustCompile(alphaUnicodeRegexString) - alphaUnicodeNumericRegex = regexp.MustCompile(alphaUnicodeNumericRegexString) - numericRegex = regexp.MustCompile(numericRegexString) - numberRegex = regexp.MustCompile(numberRegexString) - hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString) - hexcolorRegex = regexp.MustCompile(hexcolorRegexString) - rgbRegex = regexp.MustCompile(rgbRegexString) - rgbaRegex = regexp.MustCompile(rgbaRegexString) - hslRegex = regexp.MustCompile(hslRegexString) - hslaRegex = regexp.MustCompile(hslaRegexString) - emailRegex = regexp.MustCompile(emailRegexString) - base64Regex = regexp.MustCompile(base64RegexString) - iSBN10Regex = regexp.MustCompile(iSBN10RegexString) - iSBN13Regex = regexp.MustCompile(iSBN13RegexString) - uUID3Regex = regexp.MustCompile(uUID3RegexString) - uUID4Regex = regexp.MustCompile(uUID4RegexString) - uUID5Regex = regexp.MustCompile(uUID5RegexString) - uUIDRegex = regexp.MustCompile(uUIDRegexString) - aSCIIRegex = regexp.MustCompile(aSCIIRegexString) - printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString) - multibyteRegex = regexp.MustCompile(multibyteRegexString) - dataURIRegex = regexp.MustCompile(dataURIRegexString) - latitudeRegex = regexp.MustCompile(latitudeRegexString) - longitudeRegex = regexp.MustCompile(longitudeRegexString) - sSNRegex = regexp.MustCompile(sSNRegexString) - hostnameRegex = regexp.MustCompile(hostnameRegexString) + alphaRegex = regexp.MustCompile(alphaRegexString) + alphaNumericRegex = regexp.MustCompile(alphaNumericRegexString) + alphaUnicodeRegex = regexp.MustCompile(alphaUnicodeRegexString) + alphaUnicodeNumericRegex = regexp.MustCompile(alphaUnicodeNumericRegexString) + numericRegex = regexp.MustCompile(numericRegexString) + numberRegex = regexp.MustCompile(numberRegexString) + hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString) + hexcolorRegex = regexp.MustCompile(hexcolorRegexString) + rgbRegex = regexp.MustCompile(rgbRegexString) + rgbaRegex = regexp.MustCompile(rgbaRegexString) + hslRegex = regexp.MustCompile(hslRegexString) + hslaRegex = regexp.MustCompile(hslaRegexString) + emailRegex = regexp.MustCompile(emailRegexString) + base64Regex = regexp.MustCompile(base64RegexString) + base64URLRegex = regexp.MustCompile(base64URLRegexString) + iSBN10Regex = regexp.MustCompile(iSBN10RegexString) + iSBN13Regex = regexp.MustCompile(iSBN13RegexString) + uUID3Regex = regexp.MustCompile(uUID3RegexString) + uUID4Regex = regexp.MustCompile(uUID4RegexString) + uUID5Regex = regexp.MustCompile(uUID5RegexString) + uUIDRegex = regexp.MustCompile(uUIDRegexString) + uUID3RFC4122Regex = regexp.MustCompile(uUID3RFC4122RegexString) + uUID4RFC4122Regex = regexp.MustCompile(uUID4RFC4122RegexString) + uUID5RFC4122Regex = regexp.MustCompile(uUID5RFC4122RegexString) + uUIDRFC4122Regex = regexp.MustCompile(uUIDRFC4122RegexString) + aSCIIRegex = regexp.MustCompile(aSCIIRegexString) + printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString) + multibyteRegex = regexp.MustCompile(multibyteRegexString) + dataURIRegex = regexp.MustCompile(dataURIRegexString) + latitudeRegex = regexp.MustCompile(latitudeRegexString) + longitudeRegex = regexp.MustCompile(longitudeRegexString) + sSNRegex = regexp.MustCompile(sSNRegexString) + hostnameRegexRFC952 = regexp.MustCompile(hostnameRegexStringRFC952) + hostnameRegexRFC1123 = regexp.MustCompile(hostnameRegexStringRFC1123) + btcAddressRegex = regexp.MustCompile(btcAddressRegexString) + btcUpperAddressRegexBech32 = regexp.MustCompile(btcAddressUpperRegexStringBech32) + btcLowerAddressRegexBech32 = regexp.MustCompile(btcAddressLowerRegexStringBech32) + ethAddressRegex = regexp.MustCompile(ethAddressRegexString) + ethaddressRegexUpper = regexp.MustCompile(ethAddressUpperRegexString) + ethAddressRegexLower = regexp.MustCompile(ethAddressLowerRegexString) + uRLEncodedRegex = regexp.MustCompile(uRLEncodedRegexString) + hTMLEncodedRegex = regexp.MustCompile(hTMLEncodedRegexString) + hTMLRegex = regexp.MustCompile(hTMLRegexString) ) diff --git a/vendor/gopkg.in/go-playground/validator.v9/struct_level.go b/vendor/gopkg.in/go-playground/validator.v9/struct_level.go index 16c620d3d..57691ee38 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/struct_level.go +++ b/vendor/gopkg.in/go-playground/validator.v9/struct_level.go @@ -9,10 +9,10 @@ import ( type StructLevelFunc func(sl StructLevel) // StructLevelFuncCtx accepts all values needed for struct level validation -// but also allows passing of contextual validation information vi context.Context. +// but also allows passing of contextual validation information via context.Context. type StructLevelFuncCtx func(ctx context.Context, sl StructLevel) -// wrapStructLevelFunc wraps noramal StructLevelFunc makes it compatible with StructLevelFuncCtx +// wrapStructLevelFunc wraps normal StructLevelFunc makes it compatible with StructLevelFuncCtx func wrapStructLevelFunc(fn StructLevelFunc) StructLevelFuncCtx { return func(ctx context.Context, sl StructLevel) { fn(sl) @@ -23,8 +23,8 @@ func wrapStructLevelFunc(fn StructLevelFunc) StructLevelFuncCtx { // to validate a struct type StructLevel interface { - // returns the main validation object, in case one want to call validations internally. - // this is so you don;t have to use anonymous functoins to get access to the validate + // returns the main validation object, in case one wants to call validations internally. + // this is so you don't have to use anonymous functions to get access to the validate // instance. Validator() *Validate @@ -39,7 +39,7 @@ type StructLevel interface { // ExtractType gets the actual underlying type of field value. // It will dive into pointers, customTypes and return you the - // underlying value and it's kind. + // underlying value and its kind. ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool) // reports an error just by passing the field and tag information @@ -47,7 +47,7 @@ type StructLevel interface { // NOTES: // // fieldName and altName get appended to the existing namespace that - // validator is on. eg. pass 'FirstName' or 'Names[0]' depending + // validator is on. e.g. pass 'FirstName' or 'Names[0]' depending // on the nesting // // tag can be an existing validation tag or just something you make up @@ -60,7 +60,7 @@ type StructLevel interface { // // relativeNamespace and relativeActualNamespace get appended to the // existing namespace that validator is on. - // eg. pass 'User.FirstName' or 'Users[0].FirstName' depending + // e.g. pass 'User.FirstName' or 'Users[0].FirstName' depending // on the nesting. most of the time they will be blank, unless you validate // at a level lower the the current field depth ReportValidationErrors(relativeNamespace, relativeActualNamespace string, errs ValidationErrors) diff --git a/vendor/gopkg.in/go-playground/validator.v9/translations.go b/vendor/gopkg.in/go-playground/validator.v9/translations.go index 4465abbbb..4d9d75c13 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/translations.go +++ b/vendor/gopkg.in/go-playground/validator.v9/translations.go @@ -7,5 +7,5 @@ import ut "github.com/go-playground/universal-translator" type TranslationFunc func(ut ut.Translator, fe FieldError) string // RegisterTranslationsFunc allows for registering of translations -// for a 'ut.Translator' for use withing the 'TranslationFunc' +// for a 'ut.Translator' for use within the 'TranslationFunc' type RegisterTranslationsFunc func(ut ut.Translator) error diff --git a/vendor/gopkg.in/go-playground/validator.v9/util.go b/vendor/gopkg.in/go-playground/validator.v9/util.go index a01d4b16e..16a5517c9 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/util.go +++ b/vendor/gopkg.in/go-playground/validator.v9/util.go @@ -80,7 +80,7 @@ BEGIN: typ := current.Type() fld := namespace - ns := namespace + var ns string if typ != timeType { diff --git a/vendor/gopkg.in/go-playground/validator.v9/validator.go b/vendor/gopkg.in/go-playground/validator.v9/validator.go index 5fbb1660d..67473f1e5 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/validator.go +++ b/vendor/gopkg.in/go-playground/validator.v9/validator.go @@ -14,24 +14,19 @@ type validate struct { ns []byte actualNs []byte errs ValidationErrors + includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise + ffn FilterFunc + slflParent reflect.Value // StructLevel & FieldLevel + slCurrent reflect.Value // StructLevel & FieldLevel + flField reflect.Value // StructLevel & FieldLevel + cf *cField // StructLevel & FieldLevel + ct *cTag // StructLevel & FieldLevel + misc []byte // misc reusable + str1 string // misc reusable + str2 string // misc reusable + fldIsPointer bool // StructLevel & FieldLevel isPartial bool hasExcludes bool - includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise - - ffn FilterFunc - - // StructLevel & FieldLevel fields - slflParent reflect.Value - slCurrent reflect.Value - flField reflect.Value - fldIsPointer bool - cf *cField - ct *cTag - - // misc reusable values - misc []byte - str1 string - str2 string } // parent and current will be the same the first run of validateStruct @@ -127,7 +122,6 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr } if kind == reflect.Invalid { - v.errs = append(v.errs, &fieldError{ v: v.v, @@ -219,8 +213,8 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr CONTINUE: // if len == 0 then validating using 'Var' or 'VarWithValue' // Var - doesn't make much sense to do it that way, should call 'Struct', but no harm... - // VarWithField - this allows for validating against each field withing the struct against a specific value - // pretty handly in certain situations + // VarWithField - this allows for validating against each field within the struct against a specific value + // pretty handy in certain situations if len(cf.name) > 0 { ns = append(append(ns, cf.altName...), '.') structNs = append(append(structNs, cf.name...), '.') diff --git a/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go b/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go index f4b49a84c..fc9db5a30 100644 --- a/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go +++ b/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go @@ -71,7 +71,7 @@ type Validate struct { structCache *structCache } -// New returns a new instacne of 'validate' with sane defaults. +// New returns a new instance of 'validate' with sane defaults. func New() *Validate { tc := new(tagCache) @@ -97,7 +97,7 @@ func New() *Validate { for k, val := range bakedInValidators { // no need to error check here, baked in will always be valid - v.registerValidation(k, wrapFunc(val), true) + _ = v.registerValidation(k, wrapFunc(val), true) } v.pool = &sync.Pool{ @@ -119,8 +119,17 @@ func (v *Validate) SetTagName(name string) { v.tagName = name } -// RegisterTagNameFunc registers a function to get another name from the -// StructField eg. the JSON name +// RegisterTagNameFunc registers a function to get alternate names for StructFields. +// +// eg. to use the names which have been specified for JSON representations of structs, rather than normal Go field names: +// +// validate.RegisterTagNameFunc(func(fld reflect.StructField) string { +// name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] +// if name == "-" { +// return "" +// } +// return name +// }) func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) { v.tagNameFunc = fn v.hasTagNameFunc = true @@ -198,6 +207,11 @@ func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...i } for _, t := range types { + tv := reflect.ValueOf(t) + if tv.Kind() == reflect.Ptr { + t = reflect.Indirect(tv).Interface() + } + v.structLevelFuncs[reflect.TypeOf(t)] = fn } } @@ -318,7 +332,7 @@ func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn Filt vd.ffn = fn // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept - vd.validateStruct(context.Background(), top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil) + vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil) if len(vd.errs) > 0 { err = vd.errs @@ -370,39 +384,37 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields . typ := val.Type() name := typ.Name() - if fields != nil { - for _, k := range fields { + for _, k := range fields { - flds := strings.Split(k, namespaceSeparator) - if len(flds) > 0 { + flds := strings.Split(k, namespaceSeparator) + if len(flds) > 0 { - vd.misc = append(vd.misc[0:0], name...) - vd.misc = append(vd.misc, '.') + vd.misc = append(vd.misc[0:0], name...) + vd.misc = append(vd.misc, '.') - for _, s := range flds { + for _, s := range flds { - idx := strings.Index(s, leftBracket) + idx := strings.Index(s, leftBracket) - if idx != -1 { - for idx != -1 { - vd.misc = append(vd.misc, s[:idx]...) - vd.includeExclude[string(vd.misc)] = struct{}{} - - idx2 := strings.Index(s, rightBracket) - idx2++ - vd.misc = append(vd.misc, s[idx:idx2]...) - vd.includeExclude[string(vd.misc)] = struct{}{} - s = s[idx2:] - idx = strings.Index(s, leftBracket) - } - } else { - - vd.misc = append(vd.misc, s...) + if idx != -1 { + for idx != -1 { + vd.misc = append(vd.misc, s[:idx]...) vd.includeExclude[string(vd.misc)] = struct{}{} - } - vd.misc = append(vd.misc, '.') + idx2 := strings.Index(s, rightBracket) + idx2++ + vd.misc = append(vd.misc, s[idx:idx2]...) + vd.includeExclude[string(vd.misc)] = struct{}{} + s = s[idx2:] + idx = strings.Index(s, leftBracket) + } + } else { + + vd.misc = append(vd.misc, s...) + vd.includeExclude[string(vd.misc)] = struct{}{} } + + vd.misc = append(vd.misc, '.') } } } @@ -491,7 +503,7 @@ func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields .. // // WARNING: a struct can be passed for validation eg. time.Time is a struct or // if you have a custom type and have registered a custom type handler, so must -// allow it; however unforseen validations will occur if trying to validate a +// allow it; however unforeseen validations will occur if trying to validate a // struct that is meant to be passed to 'validate.Struct' // // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. @@ -509,7 +521,7 @@ func (v *Validate) Var(field interface{}, tag string) error { // // WARNING: a struct can be passed for validation eg. time.Time is a struct or // if you have a custom type and have registered a custom type handler, so must -// allow it; however unforseen validations will occur if trying to validate a +// allow it; however unforeseen validations will occur if trying to validate a // struct that is meant to be passed to 'validate.Struct' // // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. @@ -543,7 +555,7 @@ func (v *Validate) VarCtx(ctx context.Context, field interface{}, tag string) (e // // WARNING: a struct can be passed for validation eg. time.Time is a struct or // if you have a custom type and have registered a custom type handler, so must -// allow it; however unforseen validations will occur if trying to validate a +// allow it; however unforeseen validations will occur if trying to validate a // struct that is meant to be passed to 'validate.Struct' // // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. @@ -562,7 +574,7 @@ func (v *Validate) VarWithValue(field interface{}, other interface{}, tag string // // WARNING: a struct can be passed for validation eg. time.Time is a struct or // if you have a custom type and have registered a custom type handler, so must -// allow it; however unforseen validations will occur if trying to validate a +// allow it; however unforeseen validations will occur if trying to validate a // struct that is meant to be passed to 'validate.Struct' // // It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. diff --git a/vendor/modules.txt b/vendor/modules.txt index 2b038e90d..f7f354042 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -3,7 +3,7 @@ github.com/NaySoftware/go-fcm # github.com/allegro/bigcache v0.0.0-20190218064605-e24eb225f156 github.com/allegro/bigcache github.com/allegro/bigcache/queue -# github.com/aristanetworks/goarista v0.0.0-20190502180301-283422fc1708 +# github.com/aristanetworks/goarista v0.0.0-20190704150520-f44d68189fd7 github.com/aristanetworks/goarista/monotime # github.com/beevik/ntp v0.2.0 github.com/beevik/ntp @@ -20,7 +20,7 @@ github.com/btcsuite/btcutil/bech32 github.com/coreos/go-semver/semver # github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew/spew -# github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea +# github.com/deckarep/golang-set v1.7.1 github.com/deckarep/golang-set # github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 github.com/edsrzf/mmap-go @@ -38,13 +38,13 @@ github.com/ethereum/go-ethereum/p2p/discv5 github.com/ethereum/go-ethereum/p2p github.com/ethereum/go-ethereum/metrics github.com/ethereum/go-ethereum +github.com/ethereum/go-ethereum/crypto/ecies github.com/ethereum/go-ethereum/p2p/enr github.com/ethereum/go-ethereum/rlp github.com/ethereum/go-ethereum/accounts/abi github.com/ethereum/go-ethereum/accounts/abi/bind github.com/ethereum/go-ethereum/core/types github.com/ethereum/go-ethereum/event -github.com/ethereum/go-ethereum/crypto/ecies github.com/ethereum/go-ethereum/core github.com/ethereum/go-ethereum/eth github.com/ethereum/go-ethereum/eth/downloader @@ -102,7 +102,7 @@ github.com/go-stack/stack # github.com/gogo/protobuf v1.2.1 github.com/gogo/protobuf/proto github.com/gogo/protobuf/io -# github.com/golang-migrate/migrate/v4 v4.4.0 +# github.com/golang-migrate/migrate/v4 v4.5.0 github.com/golang-migrate/migrate/v4/database github.com/golang-migrate/migrate/v4/source github.com/golang-migrate/migrate/v4 @@ -156,6 +156,8 @@ github.com/jbenet/goprocess/ratelimit github.com/karalabe/hid # github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b github.com/koron/go-ssdp +# github.com/leodido/go-urn v1.1.0 +github.com/leodido/go-urn # github.com/lib/pq v1.0.0 github.com/lib/pq github.com/lib/pq/oid @@ -287,10 +289,8 @@ github.com/pmezard/go-difflib/difflib github.com/prometheus/prometheus/util/flock # github.com/rjeczalik/notify v0.9.1 github.com/rjeczalik/notify -# github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00 +# github.com/rs/cors v1.6.0 github.com/rs/cors -# github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521 -github.com/rs/xhandler # github.com/russolsen/transit v0.0.0-20180705123435-0794b4c4505a github.com/russolsen/transit # github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 @@ -315,6 +315,20 @@ github.com/status-im/migrate/v4/database/sqlcipher github.com/status-im/rendezvous github.com/status-im/rendezvous/protocol github.com/status-im/rendezvous/server +# github.com/status-im/status-protocol-go v0.1.1 +github.com/status-im/status-protocol-go +github.com/status-im/status-protocol-go/encryption/multidevice +github.com/status-im/status-protocol-go/sqlite +github.com/status-im/status-protocol-go/transport/whisper/filter +github.com/status-im/status-protocol-go/encryption +github.com/status-im/status-protocol-go/encryption/sharedsecret +github.com/status-im/status-protocol-go/internal/sqlite +github.com/status-im/status-protocol-go/transport/whisper +github.com/status-im/status-protocol-go/v1 +github.com/status-im/status-protocol-go/transport/whisper/internal/sqlite +github.com/status-im/status-protocol-go/crypto +github.com/status-im/status-protocol-go/encryption/internal/sqlite +github.com/status-im/status-protocol-go/encryption/publisher # github.com/status-im/whisper v1.4.14 github.com/status-im/whisper/whisperv6 # github.com/stretchr/testify v1.3.0 @@ -347,27 +361,27 @@ go.uber.org/atomic # go.uber.org/multierr v1.1.0 go.uber.org/multierr # go.uber.org/zap v1.10.0 +go.uber.org/zap go.uber.org/zap/zapcore -go.uber.org/zap/buffer go.uber.org/zap/internal/bufferpool +go.uber.org/zap/buffer go.uber.org/zap/internal/color go.uber.org/zap/internal/exit -go.uber.org/zap -# golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 +# golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 golang.org/x/crypto/sha3 golang.org/x/crypto/ssh/terminal -golang.org/x/crypto/pbkdf2 golang.org/x/crypto/hkdf +golang.org/x/crypto/pbkdf2 golang.org/x/crypto/scrypt -golang.org/x/crypto/ripemd160 golang.org/x/crypto/curve25519 +golang.org/x/crypto/ripemd160 golang.org/x/crypto/blake2s golang.org/x/crypto/ed25519 golang.org/x/crypto/blowfish golang.org/x/crypto/ed25519/internal/edwards25519 -# golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6 -golang.org/x/net/context +# golang.org/x/net v0.0.0-20190628185345-da137c7871d7 golang.org/x/net/websocket +golang.org/x/net/context golang.org/x/net/ipv4 golang.org/x/net/html/charset golang.org/x/net/bpf @@ -400,7 +414,7 @@ golang.org/x/text/runes golang.org/x/text/internal/language golang.org/x/text/internal/language/compact golang.org/x/text/internal/tag -# gopkg.in/go-playground/validator.v9 v9.9.3 +# gopkg.in/go-playground/validator.v9 v9.29.0 gopkg.in/go-playground/validator.v9 # gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/natefinch/lumberjack.v2