From 7be28686040679675f98b93ec46fe08ac529cedc Mon Sep 17 00:00:00 2001 From: joshemb <josh@elixxir.io> Date: Fri, 21 Oct 2022 14:35:51 -0700 Subject: [PATCH] Create JS bindings for ChannelDbCipher --- go.mod | 4 +- go.sum | 6 +++ main.go | 2 + wasm/channels.go | 95 +++++++++++++++++++++++++++++++++++++++++++ wasm/channels_test.go | 41 +++++++++++++++++++ 5 files changed, 146 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 9bc4033f..422533c4 100644 --- a/go.mod +++ b/go.mod @@ -7,8 +7,8 @@ require ( github.com/hack-pad/go-indexeddb v0.2.0 github.com/pkg/errors v0.9.1 github.com/spf13/jwalterweatherman v1.1.0 - gitlab.com/elixxir/client v1.5.1-0.20221020231618-fbe7fb53bdc4 - gitlab.com/elixxir/crypto v0.0.7-0.20221020231252-3c82f61ce90f + gitlab.com/elixxir/client v1.5.1-0.20221021211331-d639eea870fc + gitlab.com/elixxir/crypto v0.0.7-0.20221021185743-d26832a1197a gitlab.com/elixxir/primitives v0.0.3-0.20221017172918-6176818d1aba gitlab.com/xx_network/crypto v0.0.5-0.20221017172404-b384a8d8b171 gitlab.com/xx_network/primitives v0.0.4-0.20221017171439-42169a3e5c0d diff --git a/go.sum b/go.sum index 86323fae..f8cd131f 100644 --- a/go.sum +++ b/go.sum @@ -636,6 +636,10 @@ gitlab.com/elixxir/client v1.5.1-0.20221020221442-96d5b780dc6c h1:Yj4wTFBa8Kzje+ gitlab.com/elixxir/client v1.5.1-0.20221020221442-96d5b780dc6c/go.mod h1:/j/GbuxAVfR5cqLqYAq5s8IgafpyHVO63efwh/Xob4w= gitlab.com/elixxir/client v1.5.1-0.20221020231618-fbe7fb53bdc4 h1:FL/iuSNqV2zNZkqeddZgOkkby4kVX/Mk1jsYZDTIZb0= gitlab.com/elixxir/client v1.5.1-0.20221020231618-fbe7fb53bdc4/go.mod h1:MxXSZ7HShzdQ383LkxaWdA7tawpKCpjJw/BHtDEDvx0= +gitlab.com/elixxir/client v1.5.1-0.20221021203835-4d25a8f65353 h1:HiF7Nxwcsk0sSTrpDvPMG0+mGxL0TjJ9J1/8GcKtiaY= +gitlab.com/elixxir/client v1.5.1-0.20221021203835-4d25a8f65353/go.mod h1:vADvJNa+AD599FSMC8UB1Adulg1b0JQu37weyfeMFvA= +gitlab.com/elixxir/client v1.5.1-0.20221021211331-d639eea870fc h1:Iv7p5HB1fJiaTzL1qi/ZHBXweMQ5lIRjf540N6Htd2o= +gitlab.com/elixxir/client v1.5.1-0.20221021211331-d639eea870fc/go.mod h1:vADvJNa+AD599FSMC8UB1Adulg1b0JQu37weyfeMFvA= gitlab.com/elixxir/comms v0.0.4-0.20221017173926-4eaa6061dfaa h1:/FEpu0N0rAyq74FkvO3uY8BcQoWLSbVPhj/s5QfscZw= gitlab.com/elixxir/comms v0.0.4-0.20221017173926-4eaa6061dfaa/go.mod h1:rW7xdbHntP2MoF3q+2+f+IR8OHol94MRyviotfR5rXg= gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c= @@ -645,6 +649,8 @@ gitlab.com/elixxir/crypto v0.0.7-0.20221017204335-9201b3672f3a h1:RxobrpD5owwdyN gitlab.com/elixxir/crypto v0.0.7-0.20221017204335-9201b3672f3a/go.mod h1:1rftbwSVdy49LkBIkPr+w+P2mDOerYeBKoZuB3r0yqI= gitlab.com/elixxir/crypto v0.0.7-0.20221020231252-3c82f61ce90f h1:Gb9CUZ4Ln3iB/qFS7paZ1AwYObdhf4aYy9RJq79lSDI= gitlab.com/elixxir/crypto v0.0.7-0.20221020231252-3c82f61ce90f/go.mod h1:1rftbwSVdy49LkBIkPr+w+P2mDOerYeBKoZuB3r0yqI= +gitlab.com/elixxir/crypto v0.0.7-0.20221021185743-d26832a1197a h1:niF6yQflBFYXKl95pJLMWQCmwH2D29lovZlTqoAzbEY= +gitlab.com/elixxir/crypto v0.0.7-0.20221021185743-d26832a1197a/go.mod h1:1rftbwSVdy49LkBIkPr+w+P2mDOerYeBKoZuB3r0yqI= gitlab.com/elixxir/ekv v0.2.1 h1:dtwbt6KmAXG2Tik5d60iDz2fLhoFBgWwST03p7T+9Is= gitlab.com/elixxir/ekv v0.2.1/go.mod h1:USLD7xeDnuZEavygdrgzNEwZXeLQJK/w1a+htpN+JEU= gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg= diff --git a/main.go b/main.go index 5da0d86b..08f31ea2 100644 --- a/main.go +++ b/main.go @@ -74,6 +74,8 @@ func main() { js.Global().Set("GetChannelInfo", js.FuncOf(wasm.GetChannelInfo)) js.Global().Set("GetShareUrlType", js.FuncOf(wasm.GetShareUrlType)) js.Global().Set("IsNicknameValid", js.FuncOf(wasm.IsNicknameValid)) + js.Global().Set("NewChannelsDatabaseCipher", + js.FuncOf(wasm.NewChannelsDatabaseCipher)) // wasm/cmix.go js.Global().Set("NewCmix", js.FuncOf(wasm.NewCmix)) diff --git a/wasm/channels.go b/wasm/channels.go index 86011d0c..299801c8 100644 --- a/wasm/channels.go +++ b/wasm/channels.go @@ -1229,3 +1229,98 @@ func (em *eventModel) UpdateSentStatus( em.updateSentStatus( uuid, utils.CopyBytesToJS(messageID), timestamp, roundID, status) } + +//////////////////////////////////////////////////////////////////////////////// +// Channel Cipher // +//////////////////////////////////////////////////////////////////////////////// + +// ChannelDbCipher wraps the [bindings.ChannelDbCipher] object so its methods +// can be wrapped to be Javascript compatible. +type ChannelDbCipher struct { + api *bindings.ChannelDbCipher +} + +// newChannelDbCipherJS creates a new Javascript compatible object +// (map[string]interface{}) that matches the [ChannelDbCipher] structure. +func newChannelDbCipherJS(api *bindings.ChannelDbCipher) map[string]interface{} { + c := ChannelDbCipher{api} + channelDbCipherMap := map[string]interface{}{ + "Encrypt": js.FuncOf(c.Encrypt), + "Decrypt": js.FuncOf(c.Decrypt), + } + + return channelDbCipherMap +} + +// NewChannelsDatabaseCipher constructs a ChannelDbCipher object. +// +// Parameters: +// - args[0] - The tracked [Cmix] object ID (int). +// - args[1] - The password for storage. This should be the same password +// passed into [NewCmix] (Uint8Array). +// - args[2] - The maximum size of a payload to be encrypted. +// A payload passed into [ChannelDbCipher.Encrypt] that is larger than +// plaintTextBlockSize will result in an error (int). +// +// Returns: +// - A JavaScript representation of the [ChannelDbCipher]. +// - Throws a TypeError if creating the cipher fails. +func NewChannelsDatabaseCipher(_ js.Value, args []js.Value) interface{} { + cmixId := args[0].Int() + password := utils.CopyBytesToGo(args[1]) + plaintTextBlockSize := args[2].Int() + + cipher, err := bindings.NewChannelsDatabaseCipher( + cmixId, password, plaintTextBlockSize) + if err != nil { + utils.Throw(utils.TypeError, err) + return nil + } + + return newChannelDbCipherJS(cipher) +} + +// Encrypt will encrypt the raw data. It will return a ciphertext. Padding is +// done on the plaintext so all encrypted data looks uniform at rest. +// +// Parameters: +// - args[0] - The data to be encrypted (Uint8Array). This must be smaller than the block +// size passed into [NewChannelsDatabaseCipher]. If it is larger, this will +// return an error. +// +// Returns: +// - The ciphertext of the plaintext passed in (Uint8Array). +// - Throws a TypeError if it fails to encrypt the plaintext. +func (c *ChannelDbCipher) Encrypt(_ js.Value, args []js.Value) interface{} { + + ciphertext, err := c.api.Encrypt(utils.CopyBytesToGo(args[0])) + if err != nil { + utils.Throw(utils.TypeError, err) + return nil + } + + return utils.CopyBytesToJS(ciphertext) + +} + +// Decrypt will decrypt the passed in encrypted value. The plaintext will +// be returned by this function. Any padding will be discarded within +// this function. +// +// Parameters: +// - args[0] - the encrypted data returned by [ChannelDbCipher.Encrypt] +// (Uint8Array). +// +// Returns: +// - The plaintext of the ciphertext passed in (Uint8Array). +// - Throws a TypeError if it fails to encrypt the plaintext. +func (c *ChannelDbCipher) Decrypt(_ js.Value, args []js.Value) interface{} { + plaintext, err := c.api.Decrypt(utils.CopyBytesToGo(args[0])) + if err != nil { + utils.Throw(utils.TypeError, err) + return nil + } + + return utils.CopyBytesToJS(plaintext) + +} \ No newline at end of file diff --git a/wasm/channels_test.go b/wasm/channels_test.go index 2e19cffd..127657af 100644 --- a/wasm/channels_test.go +++ b/wasm/channels_test.go @@ -60,6 +60,47 @@ func Test_ChannelsManagerMethods(t *testing.T) { } } +// Tests that the map representing ChannelDbCipher returned by +// newChannelDbCipherJS contains all of the methods on ChannelDbCipher. +func Test_newChannelDbCipherJS(t *testing.T) { + cipherType := reflect.TypeOf(&ChannelDbCipher{}) + + cipher := newChannelDbCipherJS(&bindings.ChannelDbCipher{}) + if len(cipher) != cipherType.NumMethod() { + t.Errorf("ChannelDbCipher JS object does not have all methods."+ + "\nexpected: %d\nreceived: %d", cipherType.NumMethod(), len(cipher)) + } + + for i := 0; i < cipherType.NumMethod(); i++ { + method := cipherType.Method(i) + + if _, exists := cipher[method.Name]; !exists { + t.Errorf("Method %s does not exist.", method.Name) + } + } +} + +// Tests that ChannelDbCipher has all the methods that +// [bindings.ChannelDbCipher] has. +func Test_ChannelDbCipherMethods(t *testing.T) { + cipherType := reflect.TypeOf(&ChannelDbCipher{}) + binCipherType := reflect.TypeOf(&bindings.ChannelDbCipher{}) + + if binCipherType.NumMethod() != cipherType.NumMethod() { + t.Errorf("WASM ChannelDbCipher object does not have all methods from "+ + "bindings.\nexpected: %d\nreceived: %d", + binCipherType.NumMethod(), cipherType.NumMethod()) + } + + for i := 0; i < binCipherType.NumMethod(); i++ { + method := binCipherType.Method(i) + + if _, exists := cipherType.MethodByName(method.Name); !exists { + t.Errorf("Method %s does not exist.", method.Name) + } + } +} + type jsIdentity struct { pubKey js.Value codeset js.Value -- GitLab