Skip to content
Snippets Groups Projects
Commit 5a7accab authored by Jake Taylor's avatar Jake Taylor :lips:
Browse files

Merge branch 'XX-4279/ChannelsDatabaseEncryption' into 'release'

Create JS bindings for ChannelDbCipher

See merge request !20
parents c64dd6fc 1e2efad0
No related branches found
No related tags found
2 merge requests!60Revert "Fail a test to be sure it works",!20Create JS bindings for ChannelDbCipher
...@@ -39,6 +39,7 @@ const dbTimeout = time.Second ...@@ -39,6 +39,7 @@ const dbTimeout = time.Second
// channel. // channel.
type wasmModel struct { type wasmModel struct {
db *idb.Database db *idb.Database
cipher cryptoChannel.Cipher
receivedMessageCB MessageReceivedCallback receivedMessageCB MessageReceivedCallback
updateMux sync.Mutex updateMux sync.Mutex
} }
...@@ -206,6 +207,16 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID, ...@@ -206,6 +207,16 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID,
timestamp time.Time, lease time.Duration, round rounds.Round, timestamp time.Time, lease time.Duration, round rounds.Round,
mType channels.MessageType, status channels.SentStatus) uint64 { mType channels.MessageType, status channels.SentStatus) uint64 {
// Handle encryption, if it is present
if w.cipher != nil {
cipherText, err := w.cipher.Encrypt([]byte(text))
if err != nil {
jww.ERROR.Printf("Failed to encrypt Message: %+v", err)
return 0
}
text = string(cipherText)
}
msgToInsert := buildMessage( msgToInsert := buildMessage(
channelID.Marshal(), messageID.Bytes(), nil, nickname, text, pubKey, channelID.Marshal(), messageID.Bytes(), nil, nickname, text, pubKey,
codeset, timestamp, lease, round.ID, mType, status) codeset, timestamp, lease, round.ID, mType, status)
......
...@@ -12,6 +12,7 @@ package indexedDb ...@@ -12,6 +12,7 @@ package indexedDb
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"gitlab.com/elixxir/xxdk-wasm/storage"
"gitlab.com/xx_network/primitives/netTime" "gitlab.com/xx_network/primitives/netTime"
"os" "os"
"strconv" "strconv"
...@@ -37,7 +38,7 @@ func dummyCallback(uint64, *id.ID, bool) {} ...@@ -37,7 +38,7 @@ func dummyCallback(uint64, *id.ID, bool) {}
func Test_wasmModel_UpdateSentStatus(t *testing.T) { func Test_wasmModel_UpdateSentStatus(t *testing.T) {
testString := "test" testString := "test"
testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1}) testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1})
eventModel, err := newWASMModel(testString, dummyCallback) eventModel, err := newWASMModel(testString, nil, dummyCallback)
if err != nil { if err != nil {
t.Fatalf("%+v", err) t.Fatalf("%+v", err)
} }
...@@ -90,7 +91,8 @@ func Test_wasmModel_UpdateSentStatus(t *testing.T) { ...@@ -90,7 +91,8 @@ func Test_wasmModel_UpdateSentStatus(t *testing.T) {
// Smoke test wasmModel.JoinChannel/wasmModel.LeaveChannel happy paths. // Smoke test wasmModel.JoinChannel/wasmModel.LeaveChannel happy paths.
func Test_wasmModel_JoinChannel_LeaveChannel(t *testing.T) { func Test_wasmModel_JoinChannel_LeaveChannel(t *testing.T) {
eventModel, err := newWASMModel("test", dummyCallback) storage.GetLocalStorage().Clear()
eventModel, err := newWASMModel("test", nil, dummyCallback)
if err != nil { if err != nil {
t.Fatalf("%+v", err) t.Fatalf("%+v", err)
} }
...@@ -129,7 +131,7 @@ func Test_wasmModel_JoinChannel_LeaveChannel(t *testing.T) { ...@@ -129,7 +131,7 @@ func Test_wasmModel_JoinChannel_LeaveChannel(t *testing.T) {
// Test UUID gets returned when different messages are added. // Test UUID gets returned when different messages are added.
func Test_wasmModel_UUIDTest(t *testing.T) { func Test_wasmModel_UUIDTest(t *testing.T) {
testString := "testHello" testString := "testHello"
eventModel, err := newWASMModel(testString, dummyCallback) eventModel, err := newWASMModel(testString, nil, dummyCallback)
if err != nil { if err != nil {
t.Fatalf("%+v", err) t.Fatalf("%+v", err)
} }
...@@ -162,8 +164,9 @@ func Test_wasmModel_UUIDTest(t *testing.T) { ...@@ -162,8 +164,9 @@ func Test_wasmModel_UUIDTest(t *testing.T) {
// Tests if the same message ID being sent always returns the same UUID. // Tests if the same message ID being sent always returns the same UUID.
func Test_wasmModel_DuplicateReceives(t *testing.T) { func Test_wasmModel_DuplicateReceives(t *testing.T) {
storage.GetLocalStorage().Clear()
testString := "testHello" testString := "testHello"
eventModel, err := newWASMModel(testString, dummyCallback) eventModel, err := newWASMModel(testString, nil, dummyCallback)
if err != nil { if err != nil {
t.Fatalf("%+v", err) t.Fatalf("%+v", err)
} }
...@@ -200,7 +203,7 @@ func Test_wasmModel_deleteMsgByChannel(t *testing.T) { ...@@ -200,7 +203,7 @@ func Test_wasmModel_deleteMsgByChannel(t *testing.T) {
testString := "test_deleteMsgByChannel" testString := "test_deleteMsgByChannel"
totalMessages := 10 totalMessages := 10
expectedMessages := 5 expectedMessages := 5
eventModel, err := newWASMModel(testString, dummyCallback) eventModel, err := newWASMModel(testString, nil, dummyCallback)
if err != nil { if err != nil {
t.Fatalf("%+v", err) t.Fatalf("%+v", err)
} }
......
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
package indexedDb package indexedDb
import ( import (
"github.com/pkg/errors"
cryptoChannel "gitlab.com/elixxir/crypto/channel"
"gitlab.com/elixxir/xxdk-wasm/storage"
"syscall/js" "syscall/js"
"github.com/hack-pad/go-indexeddb/idb" "github.com/hack-pad/go-indexeddb/idb"
...@@ -37,25 +40,25 @@ type MessageReceivedCallback func(uuid uint64, channelID *id.ID, update bool) ...@@ -37,25 +40,25 @@ type MessageReceivedCallback func(uuid uint64, channelID *id.ID, update bool)
// NewWASMEventModelBuilder returns an EventModelBuilder which allows // NewWASMEventModelBuilder returns an EventModelBuilder which allows
// the channel manager to define the path but the callback is the same // the channel manager to define the path but the callback is the same
// across the board. // across the board.
func NewWASMEventModelBuilder( func NewWASMEventModelBuilder(encryption cryptoChannel.Cipher,
cb MessageReceivedCallback) channels.EventModelBuilder { cb MessageReceivedCallback) channels.EventModelBuilder {
fn := func(path string) (channels.EventModel, error) { fn := func(path string) (channels.EventModel, error) {
return NewWASMEventModel(path, cb) return NewWASMEventModel(path, encryption, cb)
} }
return fn return fn
} }
// NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel. // NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel.
// The name should be a base64 encoding of the users public key. // The name should be a base64 encoding of the users public key.
func NewWASMEventModel(path string, cb MessageReceivedCallback) ( func NewWASMEventModel(path string, encryption cryptoChannel.Cipher,
channels.EventModel, error) { cb MessageReceivedCallback) (channels.EventModel, error) {
databaseName := path + databaseSuffix databaseName := path + databaseSuffix
return newWASMModel(databaseName, cb) return newWASMModel(databaseName, encryption, cb)
} }
// newWASMModel creates the given [idb.Database] and returns a wasmModel. // newWASMModel creates the given [idb.Database] and returns a wasmModel.
func newWASMModel(databaseName string, cb MessageReceivedCallback) ( func newWASMModel(databaseName string, encryption cryptoChannel.Cipher,
*wasmModel, error) { cb MessageReceivedCallback) (*wasmModel, error) {
// Attempt to open database object // Attempt to open database object
ctx, cancel := newContext() ctx, cancel := newContext()
defer cancel() defer cancel()
...@@ -112,7 +115,20 @@ func newWASMModel(databaseName string, cb MessageReceivedCallback) ( ...@@ -112,7 +115,20 @@ func newWASMModel(databaseName string, cb MessageReceivedCallback) (
return nil, err return nil, err
} }
return &wasmModel{db: db, receivedMessageCB: cb}, err encryptionStatus := encryption != nil
loadedEncryptionStatus, err := storage.StoreIndexedDbEncryptionStatus(
databaseName, encryptionStatus)
if err != nil {
return nil, err
}
if encryptionStatus != loadedEncryptionStatus {
return nil, errors.New(
"Cannot load database with different encryption status.")
} else if !encryptionStatus {
jww.WARN.Printf("IndexedDb encryption disabled!")
}
return &wasmModel{db: db, receivedMessageCB: cb, cipher: encryption}, err
} }
// v1Upgrade performs the v0 -> v1 database upgrade. // v1Upgrade performs the v0 -> v1 database upgrade.
......
...@@ -70,6 +70,10 @@ func main() { ...@@ -70,6 +70,10 @@ func main() {
js.FuncOf(wasm.NewChannelsManagerWithIndexedDb)) js.FuncOf(wasm.NewChannelsManagerWithIndexedDb))
js.Global().Set("LoadChannelsManagerWithIndexedDb", js.Global().Set("LoadChannelsManagerWithIndexedDb",
js.FuncOf(wasm.LoadChannelsManagerWithIndexedDb)) js.FuncOf(wasm.LoadChannelsManagerWithIndexedDb))
js.Global().Set("LoadChannelsManagerWithIndexedDbUnsafe",
js.FuncOf(wasm.LoadChannelsManagerWithIndexedDbUnsafe))
js.Global().Set("NewChannelsManagerWithIndexedDbUnsafe",
js.FuncOf(wasm.NewChannelsManagerWithIndexedDbUnsafe))
js.Global().Set("GenerateChannel", js.FuncOf(wasm.GenerateChannel)) js.Global().Set("GenerateChannel", js.FuncOf(wasm.GenerateChannel))
js.Global().Set("DecodePublicURL", js.FuncOf(wasm.DecodePublicURL)) js.Global().Set("DecodePublicURL", js.FuncOf(wasm.DecodePublicURL))
js.Global().Set("DecodePrivateURL", js.FuncOf(wasm.DecodePrivateURL)) js.Global().Set("DecodePrivateURL", js.FuncOf(wasm.DecodePrivateURL))
...@@ -77,6 +81,8 @@ func main() { ...@@ -77,6 +81,8 @@ func main() {
js.Global().Set("GetChannelInfo", js.FuncOf(wasm.GetChannelInfo)) js.Global().Set("GetChannelInfo", js.FuncOf(wasm.GetChannelInfo))
js.Global().Set("GetShareUrlType", js.FuncOf(wasm.GetShareUrlType)) js.Global().Set("GetShareUrlType", js.FuncOf(wasm.GetShareUrlType))
js.Global().Set("IsNicknameValid", js.FuncOf(wasm.IsNicknameValid)) js.Global().Set("IsNicknameValid", js.FuncOf(wasm.IsNicknameValid))
js.Global().Set("NewChannelsDatabaseCipher",
js.FuncOf(wasm.NewChannelsDatabaseCipher))
// wasm/cmix.go // wasm/cmix.go
js.Global().Set("NewCmix", js.FuncOf(wasm.NewCmix)) js.Global().Set("NewCmix", js.FuncOf(wasm.NewCmix))
......
////////////////////////////////////////////////////////////////////////////////
// Copyright © 2022 xx foundation //
// //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file. //
////////////////////////////////////////////////////////////////////////////////
//go:build js && wasm
package storage
import (
"github.com/pkg/errors"
"os"
)
// Key to store if the database is encrypted or not
const databaseEncryptionToggleKey = "xxdkWasmDatabaseEncryptionToggle/"
// StoreIndexedDbEncryptionStatus stores the encryption status if it has not
// been previously saved. If it has, it returns its value.
func StoreIndexedDbEncryptionStatus(
databaseName string, encryption bool) (bool, error) {
data, err := GetLocalStorage().GetItem(
databaseEncryptionToggleKey + databaseName)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
GetLocalStorage().SetItem(
databaseEncryptionToggleKey+databaseName, []byte{1})
return encryption, nil
} else {
return false, err
}
}
return data[0] == 1, nil
}
...@@ -17,7 +17,7 @@ import ( ...@@ -17,7 +17,7 @@ import (
) )
// SEMVER is the current semantic version of xxDK WASM. // SEMVER is the current semantic version of xxDK WASM.
const SEMVER = "0.0.0" const SEMVER = "0.1.0"
// Storage keys. // Storage keys.
const ( const (
......
...@@ -325,21 +325,72 @@ func LoadChannelsManager(_ js.Value, args []js.Value) interface{} { ...@@ -325,21 +325,72 @@ func LoadChannelsManager(_ js.Value, args []js.Value) interface{} {
// returned as an int and the channelID as a Uint8Array. The row in the // returned as an int and the channelID as a Uint8Array. The row in the
// database that was updated can be found using the UUID. The channel ID is // database that was updated can be found using the UUID. The channel ID is
// provided so that the recipient can filter if they want to the processes // provided so that the recipient can filter if they want to the processes
// the update now or not. An "update" bool is present which tells you if the
// row is new or if it is an edited old row.
// - args[3] - ID of [ChannelDbCipher] object in tracker (int). Create this
// object with [NewChannelsDatabaseCipher] and get its id with
// [ChannelDbCipher.GetID].
//
// Returns a promise:
// - Resolves to a Javascript representation of the [ChannelsManager] object.
// - Rejected with an error if loading indexedDb or the manager fails.
// - Throws a TypeError if the cipher ID does not correspond to a cipher.
func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) interface{} {
cmixID := args[0].Int()
privateIdentity := utils.CopyBytesToGo(args[1])
cipherID := args[3].Int()
cipher, err := bindings.GetChannelDbCipherTrackerFromID(cipherID)
if err != nil {
utils.Throw(utils.TypeError, err)
}
return newChannelsManagerWithIndexedDb(cmixID, privateIdentity, args[2], cipher)
}
// NewChannelsManagerWithIndexedDbUnsafe creates a new [ChannelsManager] from a
// new private identity ([channel.PrivateIdentity]) and using indexedDb as a
// backend to manage the event model. However, the data is written in plain text
// and not encrypted. It is recommended that you do not use this in production.
//
// This is for creating a manager for an identity for the first time. For
// generating a new one channel identity, use [GenerateChannelIdentity]. To
// reload this channel manager, use [LoadChannelsManagerWithIndexedDbUnsafe],
// passing in the storage tag retrieved by [ChannelsManager.GetStorageTag].
//
// This function initialises an indexedDb database.
//
// Parameters:
// - args[0] - ID of [Cmix] object in tracker (int). This can be retrieved
// using [Cmix.GetID].
// - args[1] - Bytes of a private identity ([channel.PrivateIdentity]) that is
// generated by [GenerateChannelIdentity] (Uint8Array).
// - args[2] - Function that takes in the same parameters as
// [indexedDb.MessageReceivedCallback]. On the Javascript side, the UUID is
// returned as an int and the channelID as a Uint8Array. The row in the
// database that was updated can be found using the UUID. The channel ID is
// provided so that the recipient can filter if they want to the processes
// the update now or not. An "update" bool is present which tells you if // the update now or not. An "update" bool is present which tells you if
// the row is new or if it is an edited old row // the row is new or if it is an edited old row
// //
// Returns a promise: // Returns a promise:
// - Resolves to a Javascript representation of the [ChannelsManager] object. // - Resolves to a Javascript representation of the [ChannelsManager] object.
// - Rejected with an error if loading indexedDb or the manager fails. // - Rejected with an error if loading indexedDb or the manager fails.
func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) interface{} { func NewChannelsManagerWithIndexedDbUnsafe(_ js.Value, args []js.Value) interface{} {
cmixID := args[0].Int() cmixID := args[0].Int()
privateIdentity := utils.CopyBytesToGo(args[1]) privateIdentity := utils.CopyBytesToGo(args[1])
return newChannelsManagerWithIndexedDb(cmixID, privateIdentity, args[2], nil)
}
func newChannelsManagerWithIndexedDb(cmixID int, privateIdentity []byte,
cb js.Value, cipher *bindings.ChannelDbCipher) interface{} {
fn := func(uuid uint64, channelID *id.ID, update bool) { fn := func(uuid uint64, channelID *id.ID, update bool) {
args[2].Invoke(uuid, utils.CopyBytesToJS(channelID.Marshal()), update) cb.Invoke(uuid, utils.CopyBytesToJS(channelID.Marshal()), update)
} }
model := indexedDb.NewWASMEventModelBuilder(fn) model := indexedDb.NewWASMEventModelBuilder(cipher, fn)
promiseFn := func(resolve, reject func(args ...interface{}) js.Value) { promiseFn := func(resolve, reject func(args ...interface{}) js.Value) {
cm, err := bindings.NewChannelsManagerGoEventModel( cm, err := bindings.NewChannelsManagerGoEventModel(
...@@ -372,21 +423,69 @@ func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) interface{} { ...@@ -372,21 +423,69 @@ func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) interface{} {
// returned as an int and the channelID as a Uint8Array. The row in the // returned as an int and the channelID as a Uint8Array. The row in the
// database that was updated can be found using the UUID. The channel ID is // database that was updated can be found using the UUID. The channel ID is
// provided so that the recipient can filter if they want to the processes // provided so that the recipient can filter if they want to the processes
// the update now or not. An "update" bool is present which tells you if the
// row is new or if it is an edited old row.
// - args[3] - ID of [ChannelDbCipher] object in tracker (int). Create this
// object with [NewChannelsDatabaseCipher] and get its id with
// [ChannelDbCipher.GetID].
//
// Returns a promise:
// - Resolves to a Javascript representation of the [ChannelsManager] object.
// - Rejected with an error if loading indexedDb or the manager fails.
// - Throws a TypeError if the cipher ID does not correspond to a cipher.
func LoadChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) interface{} {
cmixID := args[0].Int()
storageTag := args[1].String()
cipherID := args[3].Int()
cipher, err := bindings.GetChannelDbCipherTrackerFromID(cipherID)
if err != nil {
utils.Throw(utils.TypeError, err)
}
return loadChannelsManagerWithIndexedDb(cmixID, storageTag, args[2], cipher)
}
// LoadChannelsManagerWithIndexedDbUnsafe loads an existing [ChannelsManager]
// using an existing indexedDb database as a backend to manage the event model.
// This should only be used to load unsafe channel managers created by
// [NewChannelsManagerWithIndexedDbUnsafe].
//
// This is for loading a manager for an identity that has already been created.
// The channel manager should have previously been created with
// [NewChannelsManagerWithIndexedDb] and the storage is retrievable with
// [ChannelsManager.GetStorageTag].
//
// Parameters:
// - args[0] - ID of [Cmix] object in tracker (int). This can be retrieved
// using [Cmix.GetID].
// - args[1] - The storage tag associated with the previously created channel
// manager and retrieved with [ChannelsManager.GetStorageTag] (string).
// - args[2] - Function that takes in the same parameters as
// [indexedDb.MessageReceivedCallback]. On the Javascript side, the UUID is
// returned as an int and the channelID as a Uint8Array. The row in the
// database that was updated can be found using the UUID. The channel ID is
// provided so that the recipient can filter if they want to the processes
// the update now or not. An "update" bool is present which tells you if // the update now or not. An "update" bool is present which tells you if
// the row is new or if it is an edited old row // the row is new or if it is an edited old row
// //
// Returns a promise: // Returns a promise:
// - Resolves to a Javascript representation of the [ChannelsManager] object. // - Resolves to a Javascript representation of the [ChannelsManager] object.
// - Rejected with an error if loading indexedDb or the manager fails. // - Rejected with an error if loading indexedDb or the manager fails.
func LoadChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) interface{} { func LoadChannelsManagerWithIndexedDbUnsafe(_ js.Value, args []js.Value) interface{} {
cmixID := args[0].Int() cmixID := args[0].Int()
storageTag := args[1].String() storageTag := args[1].String()
return loadChannelsManagerWithIndexedDb(cmixID, storageTag, args[2], nil)
}
func loadChannelsManagerWithIndexedDb(cmixID int, storageTag string,
cb js.Value, cipher *bindings.ChannelDbCipher) interface{} {
fn := func(uuid uint64, channelID *id.ID, updated bool) { fn := func(uuid uint64, channelID *id.ID, updated bool) {
args[2].Invoke(uuid, utils.CopyBytesToJS(channelID.Marshal()), updated) cb.Invoke(uuid, utils.CopyBytesToJS(channelID.Marshal()), updated)
} }
model := indexedDb.NewWASMEventModelBuilder(fn) model := indexedDb.NewWASMEventModelBuilder(cipher, fn)
promiseFn := func(resolve, reject func(args ...interface{}) js.Value) { promiseFn := func(resolve, reject func(args ...interface{}) js.Value) {
cm, err := bindings.LoadChannelsManagerGoEventModel( cm, err := bindings.LoadChannelsManagerGoEventModel(
...@@ -1274,3 +1373,108 @@ func (em *eventModel) UpdateSentStatus( ...@@ -1274,3 +1373,108 @@ func (em *eventModel) UpdateSentStatus(
em.updateSentStatus( em.updateSentStatus(
uuid, utils.CopyBytesToJS(messageID), timestamp, roundID, status) uuid, utils.CopyBytesToJS(messageID), timestamp, roundID, status)
} }
////////////////////////////////////////////////////////////////////////////////
// Channel Cipher //
////////////////////////////////////////////////////////////////////////////////
// ChannelDbCipher wraps the [bindings.ChannelDbCipher] object so its methods
// can be wrapped to be Javascript compatible.
type ChannelDbCipher struct {
api *bindings.ChannelDbCipher
}
// newChannelDbCipherJS creates a new Javascript compatible object
// (map[string]interface{}) that matches the [ChannelDbCipher] structure.
func newChannelDbCipherJS(api *bindings.ChannelDbCipher) map[string]interface{} {
c := ChannelDbCipher{api}
channelDbCipherMap := map[string]interface{}{
"GetID": js.FuncOf(c.GetID),
"Encrypt": js.FuncOf(c.Encrypt),
"Decrypt": js.FuncOf(c.Decrypt),
}
return channelDbCipherMap
}
// NewChannelsDatabaseCipher constructs a [ChannelDbCipher] object.
//
// Parameters:
// - args[0] - The tracked [Cmix] object ID (int).
// - args[1] - The password for storage. This should be the same password
// passed into [NewCmix] (Uint8Array).
// - args[2] - The maximum size of a payload to be encrypted. A payload passed
// into [ChannelDbCipher.Encrypt] that is larger than this value will result
// in an error (int).
//
// Returns:
// - JavaScript representation of the [ChannelDbCipher] object.
// - Throws a TypeError if creating the cipher fails.
func NewChannelsDatabaseCipher(_ js.Value, args []js.Value) interface{} {
cmixId := args[0].Int()
password := utils.CopyBytesToGo(args[1])
plaintTextBlockSize := args[2].Int()
cipher, err := bindings.NewChannelsDatabaseCipher(
cmixId, password, plaintTextBlockSize)
if err != nil {
utils.Throw(utils.TypeError, err)
return nil
}
return newChannelDbCipherJS(cipher)
}
// GetID returns the ID for this [bindings.ChannelDbCipher] in the
// channelDbCipherTracker.
//
// Returns:
// - Tracker ID (int).
func (c *ChannelDbCipher) GetID(js.Value, []js.Value) interface{} {
return c.api.GetID()
}
// Encrypt will encrypt the raw data. It will return a ciphertext. Padding is
// done on the plaintext so all encrypted data looks uniform at rest.
//
// Parameters:
// - args[0] - The data to be encrypted (Uint8Array). This must be smaller than
// the block size passed into [NewChannelsDatabaseCipher]. If it is larger,
// this will return an error.
//
// Returns:
// - The ciphertext of the plaintext passed in (Uint8Array).
// - Throws a TypeError if it fails to encrypt the plaintext.
func (c *ChannelDbCipher) Encrypt(_ js.Value, args []js.Value) interface{} {
ciphertext, err := c.api.Encrypt(utils.CopyBytesToGo(args[0]))
if err != nil {
utils.Throw(utils.TypeError, err)
return nil
}
return utils.CopyBytesToJS(ciphertext)
}
// Decrypt will decrypt the passed in encrypted value. The plaintext will be
// returned by this function. Any padding will be discarded within this
// function.
//
// Parameters:
// - args[0] - the encrypted data returned by [ChannelDbCipher.Encrypt]
// (Uint8Array).
//
// Returns:
// - The plaintext of the ciphertext passed in (Uint8Array).
// - Throws a TypeError if it fails to encrypt the plaintext.
func (c *ChannelDbCipher) Decrypt(_ js.Value, args []js.Value) interface{} {
plaintext, err := c.api.Decrypt(utils.CopyBytesToGo(args[0]))
if err != nil {
utils.Throw(utils.TypeError, err)
return nil
}
return utils.CopyBytesToJS(plaintext)
}
...@@ -60,6 +60,47 @@ func Test_ChannelsManagerMethods(t *testing.T) { ...@@ -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 { type jsIdentity struct {
pubKey js.Value pubKey js.Value
codeset js.Value codeset js.Value
......
...@@ -42,6 +42,7 @@ func TestPublicFunctions(t *testing.T) { ...@@ -42,6 +42,7 @@ func TestPublicFunctions(t *testing.T) {
"NewEventModel": {}, "NewEventModel": {},
"NewChannelsManagerGoEventModel": {}, "NewChannelsManagerGoEventModel": {},
"LoadChannelsManagerGoEventModel": {}, "LoadChannelsManagerGoEventModel": {},
"GetChannelDbCipherTrackerFromID": {},
// Version functions were renamed to differentiate between WASM and // Version functions were renamed to differentiate between WASM and
// client versions // client versions
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment