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