diff --git a/single/responseMessage.go b/single/responseMessage.go new file mode 100644 index 0000000000000000000000000000000000000000..a4ec404626f7837f3710760a0fd95c844e87051f --- /dev/null +++ b/single/responseMessage.go @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package single + +import ( + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" +) + +const ( + partNumLen = 1 + maxPartsLen = 1 +) + +type responseMessagePart struct { + data []byte // Serial of all contents + partNum []byte // Index of message in a series of messages + maxParts []byte // The number of parts in this message. + payload []byte // The encrypted payload +} + +// newResponseMessagePart generates a new response message part of the specified +// size. +func newResponseMessagePart(externalPayloadSize int) responseMessagePart { + if externalPayloadSize < partNumLen+maxPartsLen { + jww.FATAL.Panicf("Failed to create new single use response message "+ + "part: size of external payload (%d) is too small to contain the "+ + "message part number and max parts (%d)", + externalPayloadSize, partNumLen+maxPartsLen) + } + + return mapResponseMessagePart(make([]byte, externalPayloadSize)) +} + +// mapResponseMessagePart builds a message part mapped to the passed in data. +// It is mapped by reference; a copy is not made. +func mapResponseMessagePart(data []byte) responseMessagePart { + return responseMessagePart{ + data: data, + partNum: data[:partNumLen], + maxParts: data[partNumLen : maxPartsLen+partNumLen], + payload: data[maxPartsLen+partNumLen:], + } +} + +// unmarshalResponseMessage converts a byte buffer into a response message part. +func unmarshalResponseMessage(b []byte) (responseMessagePart, error) { + if len(b) < partNumLen+maxPartsLen { + return responseMessagePart{}, errors.Errorf("Size of passed in bytes "+ + "(%d) is too small to contain the message part number and max "+ + "parts (%d).", len(b), partNumLen+maxPartsLen) + } + return mapResponseMessagePart(b), nil +} + +// Marshal returns the bytes of the message part. +func (m responseMessagePart) Marshal() []byte { + return m.data +} + +// GetPartNum returns the index of this part in the message. +func (m responseMessagePart) GetPartNum() uint8 { + return m.partNum[0] +} + +// SetPartNum sets the part number of the message. +func (m responseMessagePart) SetPartNum(num uint8) { + copy(m.partNum, []byte{num}) +} + +// GetMaxParts returns the number of parts in the message. +func (m responseMessagePart) GetMaxParts() uint8 { + return m.maxParts[0] +} + +// SetMaxParts sets the number of parts in the message. +func (m responseMessagePart) SetMaxParts(max uint8) { + copy(m.maxParts, []byte{max}) +} + +// GetPayload returns the encrypted payload of the message part. +func (m responseMessagePart) GetPayload() []byte { + return m.payload +} + +// GetPayloadSize returns the length of the encrypted payload. +func (m responseMessagePart) GetPayloadSize() int { + return len(m.payload) +} + +// SetPayload sets the encrypted payload of the message part. +func (m responseMessagePart) SetPayload(payload []byte) { + if len(payload) != m.GetPayloadSize() { + jww.FATAL.Panicf("Failed to set payload of single use response "+ + "message part: size of supplied payload (%d) is different from "+ + "the size of the message payload (%d).", + len(payload), m.GetPayloadSize()+maxPartsLen) + } + copy(m.payload, payload) +} diff --git a/single/responseMessage_test.go b/single/responseMessage_test.go new file mode 100644 index 0000000000000000000000000000000000000000..cd3041090bfb537e94ce12fc652d2784a86dd801 --- /dev/null +++ b/single/responseMessage_test.go @@ -0,0 +1,171 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package single + +import ( + "bytes" + "math/rand" + "reflect" + "testing" +) + +// Happy path. +func Test_newResponseMessagePart(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + payloadSize := prng.Intn(2000) + expected := responseMessagePart{ + data: make([]byte, payloadSize), + partNum: make([]byte, partNumLen), + maxParts: make([]byte, maxPartsLen), + payload: make([]byte, payloadSize-partNumLen-maxPartsLen), + } + + rmp := newResponseMessagePart(payloadSize) + + if !reflect.DeepEqual(expected, rmp) { + t.Errorf("newResponseMessagePart() did not return the expected "+ + "responseMessagePart.\nexpected: %+v\nreceived: %v", expected, rmp) + } +} + +// Error path: provided payload size is not large enough. +func Test_newResponseMessagePart_PayloadSizeError(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("newResponseMessagePart() did not panic when the size of " + + "the payload is smaller than the required size.") + } + }() + + _ = newResponseMessagePart(1) +} + +// Happy path. +func Test_mapResponseMessagePart(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + expectedPartNum := uint8(prng.Uint32()) + expectedMaxParts := uint8(prng.Uint32()) + expectedPayload := make([]byte, prng.Intn(2000)) + prng.Read(expectedPayload) + var data []byte + data = append(data, expectedPartNum, expectedMaxParts) + data = append(data, expectedPayload...) + + rmp := mapResponseMessagePart(data) + + if expectedPartNum != rmp.partNum[0] { + t.Errorf("mapResponseMessagePart() did not correctly map partNum."+ + "\nexpected: %d\nreceived: %d", expectedPartNum, rmp.partNum[0]) + } + + if expectedMaxParts != rmp.maxParts[0] { + t.Errorf("mapResponseMessagePart() did not correctly map maxParts."+ + "\nexpected: %d\nreceived: %d", expectedMaxParts, rmp.maxParts[0]) + } + + if !bytes.Equal(expectedPayload, rmp.payload) { + t.Errorf("mapResponseMessagePart() did not correctly map payload."+ + "\nexpected: %+v\nreceived: %+v", expectedPayload, rmp.payload) + } + + if !bytes.Equal(data, rmp.data) { + t.Errorf("mapResponseMessagePart() did not save the data correctly."+ + "\nexpected: %+v\nreceived: %+v", data, rmp.data) + } +} + +// Happy path. +func TestResponseMessagePart_Marshal_Unmarshal(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + payload := make([]byte, prng.Intn(2000)) + prng.Read(payload) + rmp := newResponseMessagePart(prng.Intn(2000)) + + data := rmp.Marshal() + + newRmp, err := unmarshalResponseMessage(data) + if err != nil { + t.Errorf("unmarshalResponseMessage() produced an error: %+v", err) + } + + if !reflect.DeepEqual(rmp, newRmp) { + t.Errorf("Failed to Marshal() and unmarshal() the responseMessagePart."+ + "\nexpected: %+v\nrecieved: %+v", rmp, newRmp) + } +} + +// Error path: provided bytes are too small. +func Test_unmarshalResponseMessage(t *testing.T) { + _, err := unmarshalResponseMessage([]byte{1}) + if err == nil { + t.Error("unmarshalResponseMessage() did not produce an error when the " + + "byte slice is smaller required.") + } +} + +// Happy path. +func TestResponseMessagePart_SetPartNum_GetPartNum(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + expectedPartNum := uint8(prng.Uint32()) + rmp := newResponseMessagePart(prng.Intn(2000)) + + rmp.SetPartNum(expectedPartNum) + + if expectedPartNum != rmp.GetPartNum() { + t.Errorf("GetPartNum() failed to return the expected part number."+ + "\nexpected: %d\nrecieved: %d", expectedPartNum, rmp.GetPartNum()) + } +} + +// Happy path. +func TestResponseMessagePart_SetMaxParts_GetMaxParts(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + expectedMaxParts := uint8(prng.Uint32()) + rmp := newResponseMessagePart(prng.Intn(2000)) + + rmp.SetMaxParts(expectedMaxParts) + + if expectedMaxParts != rmp.GetMaxParts() { + t.Errorf("GetMaxParts() failed to return the expected max parts."+ + "\nexpected: %d\nrecieved: %d", expectedMaxParts, rmp.GetMaxParts()) + } +} + +// Happy path. +func TestResponseMessagePart_SetPayload_GetPayload_GetPayloadSize(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + externalPayloadSize := prng.Intn(2000) + payloadSize := externalPayloadSize - partNumLen - maxPartsLen + expectedPayload := make([]byte, payloadSize) + prng.Read(expectedPayload) + rmp := newResponseMessagePart(externalPayloadSize) + rmp.SetPayload(expectedPayload) + + if !bytes.Equal(expectedPayload, rmp.GetPayload()) { + t.Errorf("GetPayload() failed to return the expected payload."+ + "\nexpected: %+v\nrecieved: %+v", expectedPayload, rmp.GetPayload()) + } + + if payloadSize != rmp.GetPayloadSize() { + t.Errorf("GetPayloadSize() failed to return the expected payload size."+ + "\nexpected: %d\nrecieved: %d", payloadSize, rmp.GetPayloadSize()) + } +} + +// Error path: size of supplied payload does not match message payload size. +func TestResponseMessagePart_SetPayload_PayloadSizeError(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("SetPayload() did not panic when the size of the supplied " + + "bytes is not the same as the payload content size.") + } + }() + + rmp := newResponseMessagePart(255) + rmp.SetPayload([]byte{1, 2, 3}) +} diff --git a/single/transmitMessage.go b/single/transmitMessage.go new file mode 100644 index 0000000000000000000000000000000000000000..31e1ae5db51964ebf44623dd201e935e359bbd01 --- /dev/null +++ b/single/transmitMessage.go @@ -0,0 +1,196 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package single + +import ( + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/xx_network/primitives/id" +) + +type transmitMessage struct { + data []byte // Serial of all contents + pubKey []byte + payload []byte // The encrypted payload containing reception ID and contents +} + +// newTransmitMessage generates a new empty message for transmission that is the +// size of the specified external payload. +func newTransmitMessage(externalPayloadSize, pubKeySize int) transmitMessage { + if externalPayloadSize < pubKeySize { + jww.FATAL.Panicf("Payload size of single use transmission message "+ + "(%d) too small to contain the public key (%d).", + externalPayloadSize, pubKeySize) + } + + return mapTransmitMessage(make([]byte, externalPayloadSize), pubKeySize) +} + +// mapTransmitMessage builds a message mapped to the passed in data. It is +// mapped by reference; a copy is not made. +func mapTransmitMessage(data []byte, pubKeySize int) transmitMessage { + return transmitMessage{ + data: data, + pubKey: data[:pubKeySize], + payload: data[pubKeySize:], + } +} + +// unmarshalTransmitMessage unmarshalls a byte slice into a transmitMessage. An +// error is returned if the slice is not large enough for the public key size. +func unmarshalTransmitMessage(b []byte, pubKeySize int) (transmitMessage, error) { + if len(b) < pubKeySize { + return transmitMessage{}, errors.Errorf("Length of marshaled bytes "+ + "(%d) too small to contain public key (%d).", len(b), pubKeySize) + } + + return mapTransmitMessage(b, pubKeySize), nil +} + +// Marshal returns the serialised data of a transmitMessage. +func (m transmitMessage) Marshal() []byte { + return m.data +} + +// GetPubKey returns the public key that is part of the given group. +func (m transmitMessage) GetPubKey(grp *cyclic.Group) *cyclic.Int { + return grp.NewIntFromBytes(m.pubKey) +} + +// GetPubKeySize returns the length of the public key. +func (m transmitMessage) GetPubKeySize() int { + return len(m.pubKey) +} + +// SetPubKey saves the public key to the message as bytes. +func (m transmitMessage) SetPubKey(pubKey *cyclic.Int) { + copy(m.pubKey, pubKey.LeftpadBytes(uint64(len(m.pubKey)))) +} + +// GetPayload returns the encrypted payload of the message. +func (m transmitMessage) GetPayload() []byte { + return m.payload +} + +// GetPayloadSize returns the length of the encrypted payload. +func (m transmitMessage) GetPayloadSize() int { + return len(m.payload) +} + +// SetPayload saves the supplied bytes as the payload of the message, if the +// size is correct. +func (m transmitMessage) SetPayload(b []byte) { + if len(b) != len(m.payload) { + jww.FATAL.Panicf("Size of payload of single use transmission message "+ + "(%d) is not the same as the size of the supplied payload (%d).", + len(m.payload), len(b)) + } + + copy(m.payload, b) +} + +const numSize = 1 + +// transmitMessagePayload is the structure of transmitMessage's payload. +type transmitMessagePayload struct { + data []byte // Serial of all contents + rid []byte // Response reception ID + num []byte // Number of messages expected in response + contents []byte +} + +// newTransmitMessage generates a new empty message for transmission that is the +// size of the specified payload, which should match the size of the payload in +// the corresponding transmitMessage. +func newTransmitMessagePayload(payloadSize int) transmitMessagePayload { + if payloadSize < id.ArrIDLen+numSize { + jww.FATAL.Panicf("Size of single use transmission message payload "+ + "(%d) too small to contain the reception ID (%d) + the message "+ + "count (%d).", + payloadSize, id.ArrIDLen, numSize) + } + + return mapTransmitMessagePayload(make([]byte, payloadSize)) +} + +// mapTransmitMessagePayload builds a message payload mapped to the passed in +// data. It is mapped by reference; a copy is not made. +func mapTransmitMessagePayload(data []byte) transmitMessagePayload { + return transmitMessagePayload{ + data: data, + rid: data[:id.ArrIDLen], + num: data[id.ArrIDLen : id.ArrIDLen+numSize], + contents: data[id.ArrIDLen+numSize:], + } +} + +// unmarshalTransmitMessagePayload unmarshalls a byte slice into a +// transmitMessagePayload. An error is returned if the slice is not large enough +// for the reception ID and message count. +func unmarshalTransmitMessagePayload(b []byte) (transmitMessagePayload, error) { + if len(b) < id.ArrIDLen+numSize { + return transmitMessagePayload{}, errors.Errorf("Length of marshaled "+ + "bytes(%d) too small to contain the reception ID (%d) + the "+ + "message count (%d).", len(b), id.ArrIDLen, numSize) + } + + return mapTransmitMessagePayload(b), nil +} + +// Marshal returns the serialised data of a transmitMessagePayload. +func (mp transmitMessagePayload) Marshal() []byte { + return mp.data +} + +// GetRID returns the reception ID. +func (mp transmitMessagePayload) GetRID() *id.ID { + rid, err := id.Unmarshal(mp.rid) + if err != nil { + jww.FATAL.Panicf("Failed to unmarshal transmission ID of single use "+ + "transmission message payload: %+v", err) + } + + return rid +} + +// SetRID sets the reception ID of the payload. +func (mp transmitMessagePayload) SetRID(rid *id.ID) { + copy(mp.rid, rid.Marshal()) +} + +// GetCount returns the number of messages expected in response. +func (mp transmitMessagePayload) GetCount() uint8 { + return mp.num[0] +} + +// SetCount sets the number of expected messages. +func (mp transmitMessagePayload) SetCount(num uint8) { + copy(mp.num, []byte{num}) +} + +// GetContents returns the payload's contents. +func (mp transmitMessagePayload) GetContents() []byte { + return mp.contents +} + +// GetContentsSize returns the length of payload's contents. +func (mp transmitMessagePayload) GetContentsSize() int { + return len(mp.contents) +} + +// SetContents saves the contents to the payload, if the size is correct. +func (mp transmitMessagePayload) SetContents(b []byte) { + if len(b) != len(mp.contents) { + jww.FATAL.Panicf("Size of content of single use transmission message "+ + "payload (%d) is not the same as the size of the supplied "+ + "contents (%d).", len(mp.contents), len(b)) + } + + copy(mp.contents, b) +} diff --git a/single/transmitMessage_test.go b/single/transmitMessage_test.go new file mode 100644 index 0000000000000000000000000000000000000000..75bf80a01daea28d194a9ed49fddd4726939b37f --- /dev/null +++ b/single/transmitMessage_test.go @@ -0,0 +1,338 @@ +package single + +import ( + "bytes" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/xx_network/crypto/large" + "gitlab.com/xx_network/primitives/id" + "math/rand" + "reflect" + "testing" +) + +// Happy path. +func Test_newTransmitMessage(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + externalPayloadSize := prng.Intn(2000) + pubKeySize := prng.Intn(externalPayloadSize) + expected := transmitMessage{ + data: make([]byte, externalPayloadSize), + pubKey: make([]byte, pubKeySize), + payload: make([]byte, externalPayloadSize-pubKeySize), + } + + m := newTransmitMessage(externalPayloadSize, pubKeySize) + + if !reflect.DeepEqual(expected, m) { + t.Errorf("newTransmitMessage() did not produce the expected transmitMessage."+ + "\nexpected: %#v\nreceived: %#v", expected, m) + } +} + +// Error path: public key size is larger than external payload size. +func Test_newTransmitMessage_PubKeySizeError(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("newTransmitMessage() did not panic when the size of " + + "the payload is smaller than the size of the public key.") + } + }() + + _ = newTransmitMessage(5, 10) +} + +// Happy path. +func Test_mapTransmitMessage(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + externalPayloadSize := prng.Intn(2000) + pubKeySize := prng.Intn(externalPayloadSize) + pubKey := make([]byte, pubKeySize) + prng.Read(pubKey) + payload := make([]byte, externalPayloadSize-pubKeySize) + prng.Read(payload) + var data []byte + data = append(data, pubKey...) + data = append(data, payload...) + m := mapTransmitMessage(data, pubKeySize) + + if !bytes.Equal(data, m.data) { + t.Errorf("mapTransmitMessage() failed to map the correct bytes for data."+ + "\nexpected: %+v\nreceived: %+v", data, m.data) + } + + if !bytes.Equal(pubKey, m.pubKey) { + t.Errorf("mapTransmitMessage() failed to map the correct bytes for pubKey."+ + "\nexpected: %+v\nreceived: %+v", pubKey, m.pubKey) + } + + if !bytes.Equal(payload, m.payload) { + t.Errorf("mapTransmitMessage() failed to map the correct bytes for payload."+ + "\nexpected: %+v\nreceived: %+v", payload, m.payload) + } +} + +// Happy path. +func TestTransmitMessage_Marshal_Unmarshal(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + externalPayloadSize := prng.Intn(2000) + pubKeySize := prng.Intn(externalPayloadSize) + data := make([]byte, externalPayloadSize) + prng.Read(data) + m := mapTransmitMessage(data, pubKeySize) + + msgBytes := m.Marshal() + + newMsg, err := unmarshalTransmitMessage(msgBytes, pubKeySize) + if err != nil { + t.Errorf("unmarshalTransmitMessage produced an error: %+v", err) + } + + if !reflect.DeepEqual(m, newMsg) { + t.Errorf("Failed to marshal/unmarshal message."+ + "\nexpected: %+v\nreceived: %+v", m, newMsg) + } +} + +// Error path: public key size is larger than byte slice. +func Test_unmarshalTransmitMessage_PubKeySizeError(t *testing.T) { + _, err := unmarshalTransmitMessage([]byte{1, 2, 3}, 5) + if err == nil { + t.Error("unmarshalTransmitMessage() did not produce an error when the " + + "byte slice is smaller than the public key size.") + } +} + +// Happy path. +func TestTransmitMessage_SetPubKey_GetPubKey_GetPubKeySize(t *testing.T) { + grp := getGroup() + pubKey := grp.NewInt(5) + pubKeySize := 10 + m := newTransmitMessage(255, pubKeySize) + + m.SetPubKey(pubKey) + testPubKey := m.GetPubKey(grp) + + if pubKey.Cmp(testPubKey) != 0 { + t.Errorf("GetPubKey() failed to get correct public key."+ + "\nexpected: %s\nreceived: %s", pubKey.Text(10), testPubKey.Text(10)) + } + + if pubKeySize != m.GetPubKeySize() { + t.Errorf("GetPubKeySize() failed to return the correct size."+ + "\nexpected: %d\nreceived: %d", pubKeySize, m.GetPubKeySize()) + } +} + +func TestTransmitMessage_SetPayload_GetPayload_GetPayloadSize(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + externalPayloadSize := prng.Intn(2000) + pubKeySize := prng.Intn(externalPayloadSize) + payload := make([]byte, externalPayloadSize-pubKeySize) + prng.Read(payload) + m := newTransmitMessage(externalPayloadSize, pubKeySize) + + m.SetPayload(payload) + testPayload := m.GetPayload() + + if !bytes.Equal(payload, testPayload) { + t.Errorf("GetPayload() returned incorrect payload."+ + "\nexpected: %+v\nreceived: %+v", payload, testPayload) + } + + payloadSize := externalPayloadSize - pubKeySize + if payloadSize != m.GetPayloadSize() { + t.Errorf("GetPayloadSize() returned incorrect payload size."+ + "\nexpected: %d\nreceived: %d", payloadSize, m.GetPayloadSize()) + } +} + +// Error path: supplied payload is not the same size as message payload. +func TestTransmitMessage_SetPayload_PayloadSizeError(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("SetPayload() did not panic when the size of supplied " + + "payload is not the same size as message payload.") + } + }() + + m := newTransmitMessage(255, 10) + m.SetPayload([]byte{5}) +} + +// Happy path. +func Test_newTransmitMessagePayload(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + payloadSize := prng.Intn(2000) + expected := transmitMessagePayload{ + data: make([]byte, payloadSize), + rid: make([]byte, id.ArrIDLen), + num: make([]byte, numSize), + contents: make([]byte, payloadSize-id.ArrIDLen-numSize), + } + + mp := newTransmitMessagePayload(payloadSize) + + if !reflect.DeepEqual(expected, mp) { + t.Errorf("newTransmitMessagePayload() did not produce the expected "+ + "transmitMessagePayload.\nexpected: %#v\nreceived: %#v", expected, mp) + } +} + +// Error path: payload size is smaller than than rid size + num size. +func Test_newTransmitMessagePayload_PayloadSizeError(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("newTransmitMessagePayload() did not panic when the size " + + "of the payload is smaller than the size of the reception ID " + + "+ the message count.") + } + }() + + _ = newTransmitMessagePayload(10) +} + +// Happy path. +func Test_mapTransmitMessagePayload(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + rid := id.NewIdFromUInt(prng.Uint64(), id.User, t) + num := uint8(prng.Uint64()) + contents := make([]byte, prng.Intn(1000)) + prng.Read(contents) + var data []byte + data = append(data, rid.Bytes()...) + data = append(data, num) + data = append(data, contents...) + mp := mapTransmitMessagePayload(data) + + if !bytes.Equal(data, mp.data) { + t.Errorf("mapTransmitMessagePayload() failed to map the correct bytes "+ + "for data.\nexpected: %+v\nreceived: %+v", data, mp.data) + } + + if !bytes.Equal(rid.Bytes(), mp.rid) { + t.Errorf("mapTransmitMessagePayload() failed to map the correct bytes "+ + "for rid.\nexpected: %+v\nreceived: %+v", rid.Bytes(), mp.rid) + } + + if num != mp.num[0] { + t.Errorf("mapTransmitMessagePayload() failed to map the correct bytes "+ + "for num.\nexpected: %d\nreceived: %d", num, mp.num[0]) + } + + if !bytes.Equal(contents, mp.contents) { + t.Errorf("mapTransmitMessagePayload() failed to map the correct bytes "+ + "for contents.\nexpected: %+v\nreceived: %+v", contents, mp.contents) + } +} + +// Happy path. +func TestTransmitMessagePayload_Marshal_Unmarshal(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + data := make([]byte, prng.Intn(1000)) + prng.Read(data) + mp := mapTransmitMessagePayload(data) + + payloadBytes := mp.Marshal() + + testPayload, err := unmarshalTransmitMessagePayload(payloadBytes) + if err != nil { + t.Errorf("unmarshalTransmitMessagePayload() produced an error: %+v", err) + } + + if !reflect.DeepEqual(mp, testPayload) { + t.Errorf("Failed to marshal and unmarshal payload."+ + "\nexpected: %+v\nreceived: %+v", mp, testPayload) + } +} + +// Error path: supplied byte slice is too small for the ID and message count. +func Test_unmarshalTransmitMessagePayload(t *testing.T) { + _, err := unmarshalTransmitMessagePayload([]byte{6}) + if err == nil { + t.Error("unmarshalTransmitMessagePayload() did not return an error " + + "when the supplied byte slice was too small.") + } +} + +// Happy path. +func TestTransmitMessagePayload_SetRID_GetRID(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + mp := newTransmitMessagePayload(prng.Intn(2000)) + rid := id.NewIdFromUInt(prng.Uint64(), id.User, t) + + mp.SetRID(rid) + testRID := mp.GetRID() + + if !rid.Cmp(testRID) { + t.Errorf("GetRID() did not return the expected ID."+ + "\nexpected: %s\nreceived: %s", rid, testRID) + } +} + +// Happy path. +func TestTransmitMessagePayload_SetCount_GetCount(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + mp := newTransmitMessagePayload(prng.Intn(2000)) + count := uint8(prng.Uint64()) + + mp.SetCount(count) + testCount := mp.GetCount() + + if count != testCount { + t.Errorf("GetCount() did not return the expected count."+ + "\nexpected: %d\nreceived: %d", count, testCount) + } +} + +// Happy path. +func TestTransmitMessagePayload_SetContents_GetContents_GetContentsSize(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + payloadSize := prng.Intn(2000) + mp := newTransmitMessagePayload(payloadSize) + contentsSize := payloadSize - id.ArrIDLen - numSize + contents := make([]byte, contentsSize) + prng.Read(contents) + + mp.SetContents(contents) + testContents := mp.GetContents() + if !bytes.Equal(contents, testContents) { + t.Errorf("GetContents() did not return the expected contents."+ + "\nexpected: %+v\nreceived: %+v", contents, testContents) + } + + if contentsSize != mp.GetContentsSize() { + t.Errorf("GetContentsSize() did not return the expected size."+ + "\nexpected: %d\nreceived: %d", contentsSize, mp.GetContentsSize()) + } +} + +// Error path: supplied bytes are smaller than payload contents. +func TestTransmitMessagePayload_SetContents(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("SetContents() did not panic when the size of the " + + "supplied bytes is not the same as the payload content size.") + } + }() + + mp := newTransmitMessagePayload(255) + mp.SetContents([]byte{1, 2, 3}) +} + +func getGroup() *cyclic.Group { + return cyclic.NewGroup( + large.NewIntFromString("E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D4941"+ + "3394C049B7A8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688"+ + "B55B3DD2AEDF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E7861"+ + "575E745D31F8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC6ADC"+ + "718DD2A3E041023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C4A530E8FF"+ + "B1BC51DADDF453B0B2717C2BC6669ED76B4BDD5C9FF558E88F26E5785302BEDBC"+ + "A23EAC5ACE92096EE8A60642FB61E8F3D24990B8CB12EE448EEF78E184C7242DD"+ + "161C7738F32BF29A841698978825B4111B4BC3E1E198455095958333D776D8B2B"+ + "EEED3A1A1A221A6E37E664A64B83981C46FFDDC1A45E3D5211AAF8BFBC072768C"+ + "4F50D7D7803D2D4F278DE8014A47323631D7E064DE81C0C6BFA43EF0E6998860F"+ + "1390B5D3FEACAF1696015CB79C3F9C2D93D961120CD0E5F12CBB687EAB045241F"+ + "96789C38E89D796138E6319BE62E35D87B1048CA28BE389B575E994DCA7554715"+ + "84A09EC723742DC35873847AEF49F66E43873", 16), + large.NewIntFromString("2", 16)) +}