diff --git a/go.mod b/go.mod index 8a96df983eeffde28015eadb4575a668d554c927..422533c4666d166c10ec86bb652f21e8b63cab56 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.20221021211734-897cfe3f585e - gitlab.com/elixxir/crypto v0.0.7-0.20221021211329-2d8ef59a4df1 + 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 696dfe8c1f8479d6dbc76a443531a18f20bc256d..f8cd131fb9954d4421bedd3020e67aa71a909cb3 100644 --- a/go.sum +++ b/go.sum @@ -636,8 +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.20221021211734-897cfe3f585e h1:7EJ2AbGTGtIY+zhVj5zd0Gkh8MLQr6UxNXBFHdhJtf0= -gitlab.com/elixxir/client v1.5.1-0.20221021211734-897cfe3f585e/go.mod h1:HktYJONjuXN+qzArlk1Ul3oY2IdegZeK5U3tYQY0Xns= +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= @@ -647,8 +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.20221021211329-2d8ef59a4df1 h1:e4/JCQBNzEeVqs5XJx+QskxtvJVHt4NwVpPJa0Q+xlk= -gitlab.com/elixxir/crypto v0.0.7-0.20221021211329-2d8ef59a4df1/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 5da0d86b431568852c5f5f45cf2c88dc0af8b856..08f31ea22e609f36d3ba1372123dbca320596ee7 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 86011d0cdf5d2519ad05b2053dcb128857dbfaa7..51e4b296620ef3eae8a210004964589c7bb797bd 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 +// this value 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) + +} diff --git a/wasm/channels_test.go b/wasm/channels_test.go index 2e19cffdb62ef95ca5c3d2b70d726d619ba66797..127657af1507d6389bdf94e7fb920f7e1dddb2de 100644 --- a/wasm/channels_test.go +++ b/wasm/channels_test.go @@ -60,6 +60,47 @@ func Test_ChannelsManagerMethods(t *testing.T) { } } +// Tests that the map representing ChannelDbCipher returned by +// newChannelDbCipherJS contains all of the methods on ChannelDbCipher. +func Test_newChannelDbCipherJS(t *testing.T) { + cipherType := reflect.TypeOf(&ChannelDbCipher{}) + + cipher := newChannelDbCipherJS(&bindings.ChannelDbCipher{}) + if len(cipher) != cipherType.NumMethod() { + t.Errorf("ChannelDbCipher JS object does not have all methods."+ + "\nexpected: %d\nreceived: %d", cipherType.NumMethod(), len(cipher)) + } + + for i := 0; i < cipherType.NumMethod(); i++ { + method := cipherType.Method(i) + + if _, exists := cipher[method.Name]; !exists { + t.Errorf("Method %s does not exist.", method.Name) + } + } +} + +// Tests that ChannelDbCipher has all the methods that +// [bindings.ChannelDbCipher] has. +func Test_ChannelDbCipherMethods(t *testing.T) { + cipherType := reflect.TypeOf(&ChannelDbCipher{}) + binCipherType := reflect.TypeOf(&bindings.ChannelDbCipher{}) + + if binCipherType.NumMethod() != cipherType.NumMethod() { + t.Errorf("WASM ChannelDbCipher object does not have all methods from "+ + "bindings.\nexpected: %d\nreceived: %d", + binCipherType.NumMethod(), cipherType.NumMethod()) + } + + for i := 0; i < binCipherType.NumMethod(); i++ { + method := binCipherType.Method(i) + + if _, exists := cipherType.MethodByName(method.Name); !exists { + t.Errorf("Method %s does not exist.", method.Name) + } + } +} + type jsIdentity struct { pubKey js.Value codeset js.Value