diff --git a/single/cypher.go b/single/cypher.go index 7a2925b52d3378cd6f1f56353196830957046515..8a05758319fb01afffec8de48a4f333711d8ea46 100644 --- a/single/cypher.go +++ b/single/cypher.go @@ -56,13 +56,12 @@ func (rk *cypher) Encrypt(rp message.ResponsePart) ( } func (rk *cypher) Decrypt(contents, mac []byte) ([]byte, error) { - fp := rk.GetFingerprint() key := rk.getKey() - // Verify the CMIX message MAC + // Verify the cMix message MAC if !singleUse.VerifyMAC(key, contents, mac) { - return nil, errors.New("failed to verify the single use mac") + return nil, errors.New("failed to verify the single-use MAC") } // Decrypt the payload diff --git a/single/listener.go b/single/listener.go index 7be020b5e8812bc3dc5503659051935ab1a2c661..5501915df137881ab1c869e00e1ab28e23cd7040 100644 --- a/single/listener.go +++ b/single/listener.go @@ -63,8 +63,8 @@ func Listen(tag string, myId *id.ID, privKey *cyclic.Int, net cmix.Client, func (l *listener) Process(ecrMsg format.Message, receptionID receptionID.EphemeralIdentity, round rounds.Round) { - // Unmarshal the CMIX message contents to a transmission message - transmitMsg, err := message.UnmarshalRequest(ecrMsg.GetContents(), + // Unmarshal the cMix message contents to a request message + requestMsg, err := message.UnmarshalRequest(ecrMsg.GetContents(), l.grp.GetP().ByteLen()) if err != nil { jww.WARN.Printf("Failed to unmarshal contents on single use "+ @@ -73,25 +73,25 @@ func (l *listener) Process(ecrMsg format.Message, } // Generate DH key and symmetric key - senderPubkey := transmitMsg.GetPubKey(l.grp) + senderPubkey := requestMsg.GetPubKey(l.grp) dhKey := l.grp.Exp(senderPubkey, l.myPrivKey, l.grp.NewInt(1)) key := singleUse.NewTransmitKey(dhKey) // Verify the MAC - if !singleUse.VerifyMAC(key, transmitMsg.GetPayload(), ecrMsg.GetMac()) { + if !singleUse.VerifyMAC(key, requestMsg.GetPayload(), ecrMsg.GetMac()) { jww.WARN.Printf("mac check failed on single use request to %s "+ "on tag %s", l.myId, l.tag) return } - // Decrypt the transmission message payload + // Decrypt the request message payload fp := ecrMsg.GetKeyFP() - decryptedPayload := cAuth.Crypt(key, fp[:24], transmitMsg.GetPayload()) + decryptedPayload := cAuth.Crypt(key, fp[:24], requestMsg.GetPayload()) // Unmarshal payload payload, err := message.UnmarshalRequestPayload(decryptedPayload) if err != nil { - jww.WARN.Printf("failed to unmarshal decrypted payload on "+ + jww.WARN.Printf("[SU] Failed to unmarshal decrypted payload on "+ "single use request to %s on tag %s: %+v", l.myId, l.tag, err) return } @@ -99,7 +99,7 @@ func (l *listener) Process(ecrMsg format.Message, used := uint32(0) r := Request{ - sender: payload.GetRID(transmitMsg.GetPubKey(l.grp)), + sender: payload.GetRecipientID(requestMsg.GetPubKey(l.grp)), senderPubKey: senderPubkey, dhKey: dhKey, tag: l.tag, diff --git a/single/message/collator.go b/single/message/collator.go index 04ee11ab0e535bacc22ab4bb6f23a0e6c374e927..d2eff3a366f657e1d21f3c031cabd74a3b6f0850 100644 --- a/single/message/collator.go +++ b/single/message/collator.go @@ -6,6 +6,15 @@ import ( "sync" ) +// Error messages. +const ( + // Collate + errUnmarshalResponsePart = "failed to unmarshal response payload: %+v" + errMaxParts = "max number of parts reported by payload %d is larger than collator expected (%d)" + errPartOutOfRange = "payload part number %d greater than max number of expected parts (%d)" + errPartExists = "a payload for the part number %d already exists in the list" +) + // Initial value of the Collator maxNum that indicates it has yet to be set. const unsetCollatorMax = -1 @@ -23,16 +32,16 @@ func NewCollator(messageCount uint8) *Collator { return &Collator{ payloads: make([][]byte, messageCount), maxNum: unsetCollatorMax, + count: 0, } } // Collate collects message payload parts. Once all parts are received, the full // collated payload is returned along with true. Otherwise, returns false. func (c *Collator) Collate(payloadBytes []byte) ([]byte, bool, error) { - payload, err := UnmarshalResponse(payloadBytes) + payload, err := UnmarshalResponsePart(payloadBytes) if err != nil { - return nil, false, errors.Errorf("failed to unmarshal response "+ - "payload: %+v", err) + return nil, false, errors.Errorf(errUnmarshalResponsePart, err) } c.Lock() @@ -42,24 +51,21 @@ func (c *Collator) Collate(payloadBytes []byte) ([]byte, bool, error) { // messages expected to be received off its max number of parts if c.maxNum == unsetCollatorMax { if int(payload.GetNumParts()) > len(c.payloads) { - return nil, false, errors.Errorf("Max number of parts reported by "+ - "payload %d is larger than Collator expected (%d).", - payload.GetNumParts(), len(c.payloads)) + return nil, false, errors.Errorf( + errMaxParts, payload.GetNumParts(), len(c.payloads)) } c.maxNum = int(payload.GetNumParts()) } // Make sure that the part number is within the expected number of parts if int(payload.GetPartNum()) >= c.maxNum { - return nil, false, errors.Errorf("Payload part number (%d) greater "+ - "than max number of expected parts (%d).", - payload.GetPartNum(), c.maxNum) + return nil, false, + errors.Errorf(errPartOutOfRange, payload.GetPartNum(), c.maxNum) } // Make sure no payload with the same part number exists if c.payloads[payload.GetPartNum()] != nil { - return nil, false, errors.Errorf("A payload for the part number %d "+ - "already exists in the list.", payload.GetPartNum()) + return nil, false, errors.Errorf(errPartExists, payload.GetPartNum()) } // Add the payload to the list diff --git a/single/message/collator_test.go b/single/message/collator_test.go index 37be27c59669ad08082ac26fcbb2fc59f611c30c..9ad8312525a246fd14e9bd21f67701cd0125af91 100644 --- a/single/message/collator_test.go +++ b/single/message/collator_test.go @@ -2,19 +2,21 @@ package message import ( "bytes" + "fmt" "reflect" "strings" "testing" ) // Happy path -func Test_newCollator(t *testing.T) { +func TestNewCollator(t *testing.T) { messageCount := uint8(10) expected := &Collator{ payloads: make([][]byte, messageCount), maxNum: unsetCollatorMax, count: 0, } + c := NewCollator(messageCount) if !reflect.DeepEqual(expected, c) { @@ -24,7 +26,7 @@ func Test_newCollator(t *testing.T) { } // Happy path. -func TestCollator_collate(t *testing.T) { +func TestCollator_Collate(t *testing.T) { messageCount := 16 msgPayloadSize := 2 msgParts := map[int]ResponsePart{} @@ -74,7 +76,7 @@ func TestCollator_collate_UnmarshalError(t *testing.T) { payloadBytes := []byte{1} c := NewCollator(1) payload, collated, err := c.Collate(payloadBytes) - expectedErr := "unmarshal" + expectedErr := strings.Split(errUnmarshalResponsePart, "%")[0] if err == nil || !strings.Contains(err.Error(), expectedErr) { t.Errorf("Collate failed to return an error for failing to "+ @@ -89,32 +91,29 @@ func TestCollator_collate_UnmarshalError(t *testing.T) { } // Error path: max reported parts by payload larger than set in Collator. -func TestCollator_collate_MaxPartsError(t *testing.T) { +func TestCollator_Collate_MaxPartsError(t *testing.T) { payloadBytes := []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF} - c := NewCollator(1) - payload, collated, err := c.Collate(payloadBytes) - expectedErr := "Max number of parts" + messageCount := uint8(1) + c := NewCollator(messageCount) + _, _, err := c.Collate(payloadBytes) + expectedErr := fmt.Sprintf(errMaxParts, 0xFF, messageCount) - if err == nil || !strings.Contains(err.Error(), expectedErr) { + if err == nil || err.Error() != expectedErr { t.Errorf("Collate failed to return an error when the max number of "+ "parts is larger than the payload size."+ "\nexpected: %s\nreceived: %+v", expectedErr, err) } - - if payload != nil || collated { - t.Errorf("Collate signaled the payload was collated on error."+ - "\npayload: %+v\ncollated: %+v", payload, collated) - } } // Error path: the message part number is greater than the max number of parts. -func TestCollator_collate_PartNumTooLargeError(t *testing.T) { +func TestCollator_Collate_PartNumTooLargeError(t *testing.T) { payloadBytes := []byte{25, 5, 5, 5, 5} - c := NewCollator(5) + partNum := uint8(5) + c := NewCollator(partNum) _, _, err := c.Collate(payloadBytes) - expectedErr := "greater than max number of expected parts" + expectedErr := fmt.Sprintf(errPartOutOfRange, partNum, c.maxNum) - if err == nil || !strings.Contains(err.Error(), expectedErr) { + if err == nil || err.Error() != expectedErr { t.Errorf("Collate failed to return the expected error when the part "+ "number is greater than the max number of parts."+ "\nexpected: %s\nreceived: %+v", expectedErr, err) @@ -122,17 +121,17 @@ func TestCollator_collate_PartNumTooLargeError(t *testing.T) { } // Error path: a message with the part number already exists. -func TestCollator_collate_PartExistsError(t *testing.T) { +func TestCollator_Collate_PartExistsError(t *testing.T) { payloadBytes := []byte{0, 1, 5, 0, 1, 20} c := NewCollator(5) _, _, err := c.Collate(payloadBytes) if err != nil { t.Fatalf("Collate returned an error: %+v", err) } - expectedErr := "A payload for the part number" + expectedErr := fmt.Sprintf(errPartExists, payloadBytes[1]) _, _, err = c.Collate(payloadBytes) - if err == nil || !strings.Contains(err.Error(), expectedErr) { + if err == nil || err.Error() != expectedErr { t.Errorf("Collate failed to return an error when the part number "+ "already exists.\nexpected: %s\nreceived: %+v", expectedErr, err) } diff --git a/single/message/request.go b/single/message/request.go index d2b0280446998f67017823b9903051ba79974eb7..75e83510926c7b1d559be6068b0bde1702cae34c 100644 --- a/single/message/request.go +++ b/single/message/request.go @@ -16,22 +16,56 @@ import ( "gitlab.com/elixxir/crypto/e2e/singleUse" "gitlab.com/xx_network/primitives/id" "io" + "strconv" + "strings" +) + +// Error messages. +const ( + // NewRequest + errNewReqPayloadSize = "[SU] Failed to create new single-use request " + + "message: external payload size (%d) is smaller than the public key " + + "size (%d)." + + // UnmarshalRequest + errReqDataSize = "size of data (%d) must be at least %d" + + // Request.SetPayload + errReqPayloadSize = "[SU] Failed to set payload of single-use request " + + "message: size of the supplied payload (%d) is larger than the max " + + "message size (%d)." + + // NewRequestPayload + errNewReqPayloadPayloadSize = "[SU] Failed to create new single-use " + + "request payload message: payload size (%d) is smaller than the " + + "minimum message size for a request payload (%d)." + + // UnmarshalRequestPayload + errReqPayloadDataSize = "size of data (%d) must be at least %d" + + // RequestPayload.SetNonce + errSetReqPayloadNonce = "failed to generate nonce: %+v" + + // RequestPayload.SetContents + errReqPayloadContentsSize = "[SU] Failed to set contents of single-use " + + "request payload message: size of the supplied contents (%d) is " + + "larger than the max message size (%d)." ) /* -+-------------------------------------------------------------------------------------+ -| CMIX Message Contents | -+-----------+------------+------------------------------------------------------------+ -| Version | pubKey | payload (RequestPayload) | -| 1 byte | pubKeySize | externalPayloadSize - pubKeySize | -+-----------+------------+----------+---------+------------------+---------+----------+ - | Tag FP | nonce | maxResponseParts | size | contents | - | 16 bytes | 8 bytes | 1 byte | 2 bytes | variable | - +----------+---------+------------------+---------+----------+ ++--------------------------------------------------------------------------------------------+ +| cMix Message Contents | ++-----------+------------+-------------------------------------------------------------------+ +| Version | pubKey | payload (RequestPayload) | +| 1 byte | pubKeySize | externalPayloadSize - 1 byte - pubKeySize | ++-----------+------------+---------+-----------------+------------------+---------+----------+ + | nonce | numRequestParts | maxResponseParts | size | contents | + | 8 bytes | 1 byte | 1 byte | 2 bytes | variable | + +---------+-----------------+------------------+---------+----------+ */ -const transmitMessageVersion = 0 -const transmitMessageVersionSize = 1 +const requestVersion = 0 +const requestVersionSize = 1 type Request struct { data []byte // Serial of all contents @@ -40,23 +74,23 @@ type Request struct { payload []byte // The encrypted payload containing reception ID and contents } -// NewRequest generates a new empty message for transmission that is the -// size of the specified external payload. +// NewRequest generates a new empty message for a request that is the size of +// the specified external payload. func NewRequest(externalPayloadSize, pubKeySize int) Request { if externalPayloadSize < pubKeySize { - jww.FATAL.Panicf("Payload size of single-use transmission message "+ - "(%d) too small to contain the public key (%d).", - externalPayloadSize, pubKeySize) + jww.FATAL.Panicf(errNewReqPayloadSize, externalPayloadSize, pubKeySize) } tm := mapRequest(make([]byte, externalPayloadSize), pubKeySize) - tm.version[0] = transmitMessageVersion + tm.version[0] = requestVersion return tm } -func GetRequestPayloadSize(externalPayloadSize, pubKeySize int) uint { - return uint(externalPayloadSize - transmitMessageVersionSize - pubKeySize) +// GetRequestPayloadSize returns the size of the payload for the given external +// payload size and public key size. +func GetRequestPayloadSize(externalPayloadSize, pubKeySize int) int { + return externalPayloadSize - requestVersionSize - pubKeySize } // mapRequest builds a message mapped to the passed in data. It is @@ -64,9 +98,9 @@ func GetRequestPayloadSize(externalPayloadSize, pubKeySize int) uint { func mapRequest(data []byte, pubKeySize int) Request { return Request{ data: data, - version: data[:transmitMessageVersionSize], - pubKey: data[transmitMessageVersionSize : transmitMessageVersionSize+pubKeySize], - payload: data[transmitMessageVersionSize+pubKeySize:], + version: data[:requestVersionSize], + pubKey: data[requestVersionSize : requestVersionSize+pubKeySize], + payload: data[requestVersionSize+pubKeySize:], } } @@ -74,8 +108,7 @@ func mapRequest(data []byte, pubKeySize int) Request { // error is returned if the slice is not large enough for the public key size. func UnmarshalRequest(b []byte, pubKeySize int) (Request, error) { if len(b) < pubKeySize { - return Request{}, errors.Errorf("Length of marshaled bytes "+ - "(%d) too small to contain public key (%d).", len(b), pubKeySize) + return Request{}, errors.Errorf(errReqDataSize, len(b), pubKeySize) } return mapRequest(b, pubKeySize), nil @@ -118,22 +151,29 @@ func (m Request) GetPayloadSize() int { // SetPayload saves the supplied bytes as the payload of the message, if the // size is correct. -func (m Request) 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)) +func (m Request) SetPayload(payload []byte) { + if len(payload) != len(m.payload) { + jww.FATAL.Panicf(errReqPayloadSize, len(m.payload), len(payload)) } - copy(m.payload, b) + copy(m.payload, payload) } +/* ++-------------------------------------------------------------------+ +| Request payload | ++---------+-----------------+------------------+---------+----------+ +| nonce | numRequestParts | maxResponseParts | size | contents | +| 8 bytes | 1 byte | 1 byte | 2 bytes | variable | ++---------+-----------------+------------------+---------+----------+ +*/ + const ( nonceSize = 8 numRequestPartsSize = 1 maxResponsePartsSize = 1 sizeSize = 2 - transmitPlMinSize = nonceSize + numRequestPartsSize + maxResponsePartsSize + sizeSize + requestMinSize = nonceSize + numRequestPartsSize + maxResponsePartsSize + sizeSize ) // RequestPayload is the structure of Request's payload. @@ -146,14 +186,13 @@ type RequestPayload struct { contents []byte } -// NewRequestPayload 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 Request. +// NewRequestPayload generates a new empty message for request that is the size +// of the specified payload, which should match the size of the payload in the +// corresponding Request. func NewRequestPayload(payloadSize int, payload []byte, maxMsgs uint8) RequestPayload { - if payloadSize < transmitPlMinSize { - jww.FATAL.Panicf("Size of single-use transmission message payload "+ - "(%d) too small to contain the necessary data (%d).", - payloadSize, transmitPlMinSize) + if payloadSize < requestMinSize { + jww.FATAL.Panicf( + errNewReqPayloadPayloadSize, payloadSize, requestMinSize) } // Map fields to data @@ -164,8 +203,10 @@ func NewRequestPayload(payloadSize int, payload []byte, maxMsgs uint8) RequestPa return mp } -func GetRequestContentsSize(payloadSize uint) uint { - return payloadSize - transmitPlMinSize +// GetRequestContentsSize returns the size of the contents of a RequestPayload +// given the payload size. +func GetRequestContentsSize(payloadSize int) int { + return payloadSize - requestMinSize } // mapRequestPayload builds a message payload mapped to the passed in @@ -176,22 +217,21 @@ func mapRequestPayload(data []byte) RequestPayload { nonce: data[:nonceSize], numRequestParts: data[nonceSize : nonceSize+numRequestPartsSize], maxResponseParts: data[nonceSize+numRequestPartsSize : nonceSize+maxResponsePartsSize+numRequestPartsSize], - size: data[nonceSize+numRequestPartsSize+maxResponsePartsSize : transmitPlMinSize], - contents: data[transmitPlMinSize:], + size: data[nonceSize+numRequestPartsSize+maxResponsePartsSize : requestMinSize], + contents: data[requestMinSize:], } mp.numRequestParts[0] = 1 return mp } -// UnmarshalRequestPayload unmarshalls a byte slice into a -// RequestPayload. An error is returned if the slice is not large enough -// for the reception ID and message count. +// UnmarshalRequestPayload unmarshalls a byte slice into a RequestPayload. An +// error is returned if the slice is not large enough for the reception ID and +// message count. func UnmarshalRequestPayload(b []byte) (RequestPayload, error) { - if len(b) < transmitPlMinSize { - return RequestPayload{}, errors.Errorf("Length of marshaled "+ - "bytes(%d) too small to contain the necessary data (%d).", - len(b), transmitPlMinSize) + if len(b) < requestMinSize { + return RequestPayload{}, + errors.Errorf(errReqPayloadDataSize, len(b), requestMinSize) } return mapRequestPayload(b), nil @@ -202,8 +242,8 @@ func (mp RequestPayload) Marshal() []byte { return mp.data } -// GetRID generates the reception ID from the bytes of the payload. -func (mp RequestPayload) GetRID(pubKey *cyclic.Int) *id.ID { +// GetRecipientID generates the recipient ID from the bytes of the payload. +func (mp RequestPayload) GetRecipientID(pubKey *cyclic.Int) *id.ID { return singleUse.NewRecipientID(pubKey, mp.Marshal()) } @@ -216,28 +256,28 @@ func (mp RequestPayload) GetNonce() uint64 { // reader fails. func (mp RequestPayload) SetNonce(rng io.Reader) error { if _, err := rng.Read(mp.nonce); err != nil { - return errors.Errorf("failed to generate nonce: %+v", err) + return errors.Errorf(errSetReqPayloadNonce, err) } return nil } -// GetMaxResponseParts returns the number of messages expected in response. +// GetMaxResponseParts returns the maximum number of response messages allowed. func (mp RequestPayload) GetMaxResponseParts() uint8 { return mp.maxResponseParts[0] } -// SetMaxResponseParts sets the number of expected messages. +// SetMaxResponseParts sets the maximum number of response messages allowed. func (mp RequestPayload) SetMaxResponseParts(num uint8) { copy(mp.maxResponseParts, []byte{num}) } -// GetNumRequestParts returns the number of messages expected in the request. +// GetNumRequestParts returns the number of messages in the request. func (mp RequestPayload) GetNumRequestParts() uint8 { return mp.numRequestParts[0] } -// SetNumRequestParts sets the number of expected messages. +// SetNumRequestParts sets the number of messages in the request. func (mp RequestPayload) SetNumRequestParts(num uint8) { copy(mp.numRequestParts, []byte{num}) } @@ -261,10 +301,8 @@ func (mp RequestPayload) GetMaxContentsSize() int { // not zero out previous content. func (mp RequestPayload) SetContents(contents []byte) { if len(contents) > len(mp.contents) { - jww.FATAL.Panicf("Failed to set contents of single-use transmission "+ - "message: max size of message content (%d) is smaller than the "+ - "size of the supplied contents (%d).", - len(mp.contents), len(contents)) + jww.FATAL.Panicf( + errReqPayloadContentsSize, len(contents), len(mp.contents)) } binary.BigEndian.PutUint16(mp.size, uint16(len(contents))) @@ -272,10 +310,17 @@ func (mp RequestPayload) SetContents(contents []byte) { copy(mp.contents, contents) } -// String returns the contents for printing adhering to the stringer interface. +// String returns the contents of a RequestPayload as a human-readable string. +// This function adheres to the fmt.Stringer interface. func (mp RequestPayload) String() string { - return fmt.Sprintf("Data: %x [nonce: %x, "+ - "maxResponseParts: %x, size: %x, content: %x]", mp.data, - mp.nonce, mp.maxResponseParts, mp.size, mp.contents) + str := []string{ + "nonce: " + strconv.Itoa(int(mp.GetNonce())), + "numRequestParts: " + strconv.Itoa(int(mp.GetNumRequestParts())), + "maxResponseParts: " + strconv.Itoa(int(mp.GetMaxResponseParts())), + "size: " + strconv.Itoa(mp.GetContentsSize()), + "contents: " + fmt.Sprintf("%q", mp.GetContents()), + } + + return "{" + strings.Join(str, ", ") + "}" } diff --git a/single/message/request_test.go b/single/message/request_test.go index 2e577729ce9fe23263dd508f83b692bf37104229..b2ee1dca55ecef72b534972b2303d43d63cf252a 100644 --- a/single/message/request_test.go +++ b/single/message/request_test.go @@ -10,6 +10,7 @@ package message import ( "bytes" "encoding/binary" + "fmt" "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/e2e/singleUse" "gitlab.com/elixxir/primitives/format" @@ -21,15 +22,15 @@ import ( ) // Happy path. -func Test_newTransmitMessage(t *testing.T) { +func TestNewRequest(t *testing.T) { prng := rand.New(rand.NewSource(42)) externalPayloadSize := prng.Intn(2000) pubKeySize := prng.Intn(externalPayloadSize) expected := Request{ data: make([]byte, externalPayloadSize), - version: make([]byte, transmitMessageVersionSize), + version: make([]byte, requestVersionSize), pubKey: make([]byte, pubKeySize), - payload: make([]byte, externalPayloadSize-pubKeySize-transmitMessageVersionSize), + payload: make([]byte, externalPayloadSize-pubKeySize-requestVersionSize), } m := NewRequest(externalPayloadSize, pubKeySize) @@ -41,19 +42,23 @@ func Test_newTransmitMessage(t *testing.T) { } // Error path: public key size is larger than external payload size. -func Test_newTransmitMessage_PubKeySizeError(t *testing.T) { +func TestNewRequest_PubKeySizeError(t *testing.T) { + externalPayloadSize, pubKeySize := 5, 10 + expectedErr := fmt.Sprintf( + errNewReqPayloadSize, externalPayloadSize, pubKeySize) defer func() { - if r := recover(); r == nil || !strings.Contains(r.(string), "Payload size") { - t.Error("NewRequest did not panic when the size of " + - "the payload is smaller than the size of the public key.") + if r := recover(); r == nil || r != expectedErr { + t.Errorf("NewRequest did not panic with the expected error when " + + "the size of the payload is smaller than the size of the " + + "public key.") } }() - _ = NewRequest(5, 10) + _ = NewRequest(externalPayloadSize, pubKeySize) } // Happy path. -func Test_mapTransmitMessage(t *testing.T) { +func Test_mapRequest(t *testing.T) { prng := rand.New(rand.NewSource(42)) externalPayloadSize := prng.Intn(2000) pubKeySize := prng.Intn(externalPayloadSize) @@ -85,7 +90,7 @@ func Test_mapTransmitMessage(t *testing.T) { } // Happy path. -func TestTransmitMessage_Marshal_Unmarshal(t *testing.T) { +func TestRequest_Marshal_UnmarshalRequest(t *testing.T) { prng := rand.New(rand.NewSource(42)) externalPayloadSize := prng.Intn(2000) pubKeySize := prng.Intn(externalPayloadSize) @@ -107,7 +112,7 @@ func TestTransmitMessage_Marshal_Unmarshal(t *testing.T) { } // Error path: public key size is larger than byte slice. -func Test_unmarshalTransmitMessage_PubKeySizeError(t *testing.T) { +func Test_UnmarshalRequest_PubKeySizeError(t *testing.T) { _, err := UnmarshalRequest([]byte{1, 2, 3}, 5) if err == nil { t.Error("unmarshalRequest did not produce an error when the " + @@ -116,7 +121,7 @@ func Test_unmarshalTransmitMessage_PubKeySizeError(t *testing.T) { } // Happy path. -func TestTransmitMessage_SetPubKey_GetPubKey_GetPubKeySize(t *testing.T) { +func TestRequest_SetPubKey_GetPubKey_GetPubKeySize(t *testing.T) { grp := getGroup() pubKey := grp.NewInt(5) pubKeySize := 10 @@ -136,11 +141,11 @@ func TestTransmitMessage_SetPubKey_GetPubKey_GetPubKeySize(t *testing.T) { } } -func TestTransmitMessage_SetPayload_GetPayload_GetPayloadSize(t *testing.T) { +func TestRequest_SetPayload_GetPayload_GetPayloadSize(t *testing.T) { prng := rand.New(rand.NewSource(42)) externalPayloadSize := prng.Intn(2000) pubKeySize := prng.Intn(externalPayloadSize) - payloadSize := externalPayloadSize - pubKeySize - transmitMessageVersionSize + payloadSize := externalPayloadSize - pubKeySize - requestVersionSize payload := make([]byte, payloadSize) prng.Read(payload) m := NewRequest(externalPayloadSize, pubKeySize) @@ -160,10 +165,13 @@ func TestTransmitMessage_SetPayload_GetPayload_GetPayloadSize(t *testing.T) { } // Error path: supplied payload is not the same size as message payload. -func TestTransmitMessage_SetPayload_PayloadSizeError(t *testing.T) { - expectedErr := "is not the same as the size" +func TestRequest_SetPayload_PayloadSizeError(t *testing.T) { + m := NewRequest(255, 10) + payload := []byte{5} + expectedErr := fmt.Sprintf(errReqPayloadSize, len(m.payload), len(payload)) + defer func() { - if r := recover(); r == nil || !strings.Contains(r.(string), expectedErr) { + if r := recover(); r == nil || r != expectedErr { t.Errorf("SetContents did not panic with the expected error when "+ "the size of supplied contents is not the same size as "+ "message contents.\nexpected: %s\nreceived: %+v", @@ -171,12 +179,11 @@ func TestTransmitMessage_SetPayload_PayloadSizeError(t *testing.T) { } }() - m := NewRequest(255, 10) - m.SetPayload([]byte{5}) + m.SetPayload(payload) } // Happy path. -func Test_newTransmitMessagePayload(t *testing.T) { +func TestNewRequestPayload(t *testing.T) { prng := rand.New(rand.NewSource(42)) payloadSize := prng.Intn(2000) expected := RequestPayload{ @@ -185,17 +192,17 @@ func Test_newTransmitMessagePayload(t *testing.T) { numRequestParts: make([]byte, numRequestPartsSize), maxResponseParts: make([]byte, maxResponsePartsSize), size: make([]byte, sizeSize), - contents: make([]byte, payloadSize-transmitPlMinSize), + contents: make([]byte, payloadSize-requestMinSize), } expected.numRequestParts[0] = 1 - binary.BigEndian.PutUint16(expected.size, uint16(payloadSize-transmitPlMinSize)) + binary.BigEndian.PutUint16(expected.size, uint16(payloadSize-requestMinSize)) expected.SetMaxResponseParts(10) expected.data = append(expected.nonce, append(expected.numRequestParts, append(expected.maxResponseParts, append(expected.size, expected.contents...)...)...)...) - payload := make([]byte, payloadSize-transmitPlMinSize) + payload := make([]byte, payloadSize-requestMinSize) mp := NewRequestPayload(payloadSize, payload, 10) if !reflect.DeepEqual(expected, mp) { @@ -205,16 +212,7 @@ func Test_newTransmitMessagePayload(t *testing.T) { } // Error path: payload size is smaller than rid size + maxResponseParts size. -func Test_newTransmitMessagePayload_PayloadSizeError(t *testing.T) { - expectedErr := "Size of single-use transmission message payload" - defer func() { - if r := recover(); r == nil || !strings.Contains(r.(string), expectedErr) { - t.Errorf("NewRequestPayload did not panic with the expected error "+ - "when the size of the payload is smaller than the size of the "+ - "reception ID + the message count.\nexpected: %s\nreceived: %+v", - expectedErr, r) - } - }() +func TestNewRequestPayload_PayloadSizeError(t *testing.T) { payloadSize := 10 prng := rand.New(rand.NewSource(42)) payload := make([]byte, payloadSize) @@ -223,11 +221,23 @@ func Test_newTransmitMessagePayload_PayloadSizeError(t *testing.T) { t.Errorf("Failed to read to payload: %+v", err) } + expectedErr := fmt.Sprintf( + errNewReqPayloadPayloadSize, payloadSize, requestMinSize) + + defer func() { + if r := recover(); r == nil || r != expectedErr { + t.Errorf("NewRequestPayload did not panic with the expected error "+ + "when the size of the payload is smaller than the size of the "+ + "reception ID + the message count.\nexpected: %s\nreceived: %+v", + expectedErr, r) + } + }() + _ = NewRequestPayload(10, payload, 5) } // Happy path. -func Test_mapTransmitMessagePayload(t *testing.T) { +func Test_mapRequestPayload(t *testing.T) { prng := rand.New(rand.NewSource(42)) nonceBytes := make([]byte, nonceSize) numRequestParts := make([]byte, numRequestPartsSize) @@ -275,7 +285,7 @@ func Test_mapTransmitMessagePayload(t *testing.T) { } // Happy path. -func TestTransmitMessagePayload_Marshal_Unmarshal(t *testing.T) { +func TestRequestPayload_Marshal_UnmarshalRequestPayload(t *testing.T) { prng := rand.New(rand.NewSource(42)) data := make([]byte, prng.Intn(1000)) prng.Read(data) @@ -295,7 +305,7 @@ func TestTransmitMessagePayload_Marshal_Unmarshal(t *testing.T) { } // Error path: supplied byte slice is too small for the ID and message count. -func Test_unmarshalTransmitMessagePayload(t *testing.T) { +func Test_UnmarshalRequestPayload(t *testing.T) { _, err := UnmarshalRequestPayload([]byte{6}) if err == nil { t.Error("UnmarshalRequestPayload did not return an error " + @@ -304,10 +314,10 @@ func Test_unmarshalTransmitMessagePayload(t *testing.T) { } // Happy path. -func TestTransmitMessagePayload_GetRID(t *testing.T) { +func TestRequestPayload_GetRecipientID(t *testing.T) { prng := rand.New(rand.NewSource(42)) payloadSize := prng.Intn(2000) - payload := make([]byte, payloadSize-transmitPlMinSize) + payload := make([]byte, payloadSize-requestMinSize) _, err := prng.Read(payload) if err != nil { t.Errorf("Failed to read to payload: %+v", err) @@ -316,19 +326,19 @@ func TestTransmitMessagePayload_GetRID(t *testing.T) { mp := NewRequestPayload(payloadSize, payload, 5) expectedRID := singleUse.NewRecipientID(getGroup().NewInt(42), mp.Marshal()) - testRID := mp.GetRID(getGroup().NewInt(42)) + testRID := mp.GetRecipientID(getGroup().NewInt(42)) if !expectedRID.Cmp(testRID) { - t.Errorf("GetRID did not return the expected ID."+ + t.Errorf("GetRecipientID did not return the expected ID."+ "\nexpected: %s\nreceived: %s", expectedRID, testRID) } } // Happy path. -func Test_transmitMessagePayload_SetNonce_GetNonce(t *testing.T) { +func TestRequestPayload_SetNonce_GetNonce(t *testing.T) { prng := rand.New(rand.NewSource(42)) payloadSize := prng.Intn(2000) - payload := make([]byte, payloadSize-transmitPlMinSize) + payload := make([]byte, payloadSize-requestMinSize) _, err := prng.Read(payload) if err != nil { t.Errorf("Failed to read to payload: %+v", err) @@ -351,10 +361,10 @@ func Test_transmitMessagePayload_SetNonce_GetNonce(t *testing.T) { } // Error path: RNG return an error. -func Test_transmitMessagePayload_SetNonce_RngError(t *testing.T) { +func TestRequestPayload_SetNonce_RngError(t *testing.T) { prng := rand.New(rand.NewSource(42)) payloadSize := prng.Intn(2000) - payload := make([]byte, payloadSize-transmitPlMinSize) + payload := make([]byte, payloadSize-requestMinSize) _, err := prng.Read(payload) if err != nil { t.Errorf("Failed to read to payload: %+v", err) @@ -370,10 +380,10 @@ func Test_transmitMessagePayload_SetNonce_RngError(t *testing.T) { } // Happy path. -func TestTransmitMessagePayload_SetMaxParts_GetMaxParts(t *testing.T) { +func TestRequestPayload_SetMaxResponseParts_GetMaxResponseParts(t *testing.T) { prng := rand.New(rand.NewSource(42)) payloadSize := prng.Intn(2000) - payload := make([]byte, payloadSize-transmitPlMinSize) + payload := make([]byte, payloadSize-requestMinSize) _, err := prng.Read(payload) if err != nil { t.Errorf("Failed to read to payload: %+v", err) @@ -386,16 +396,16 @@ func TestTransmitMessagePayload_SetMaxParts_GetMaxParts(t *testing.T) { testCount := mp.GetMaxResponseParts() if count != testCount { - t.Errorf("GetNumParts did not return the expected count."+ + t.Errorf("GetNumRequestParts did not return the expected count."+ "\nexpected: %d\nreceived: %d", count, testCount) } } // Happy path. -func TestTransmitMessagePayload_SetContents_GetContents_GetContentsSize_GetMaxContentsSize(t *testing.T) { +func TestRequestPayload_GetContents_GetContentsSize_GetMaxContentsSize(t *testing.T) { prng := rand.New(rand.NewSource(42)) payloadSize := format.MinimumPrimeSize - contentsSize := (format.MinimumPrimeSize - transmitPlMinSize) / 2 + contentsSize := (format.MinimumPrimeSize - requestMinSize) / 2 contents := make([]byte, contentsSize) prng.Read(contents) @@ -412,18 +422,29 @@ func TestTransmitMessagePayload_SetContents_GetContents_GetContentsSize_GetMaxCo "\nexpected: %d\nreceived: %d", contentsSize, mp.GetContentsSize()) } - if format.MinimumPrimeSize-transmitPlMinSize != mp.GetMaxContentsSize() { + if format.MinimumPrimeSize-requestMinSize != mp.GetMaxContentsSize() { t.Errorf("GetMaxContentsSize did not return the expected size."+ "\nexpected: %d\nreceived: %d", - format.MinimumPrimeSize-transmitPlMinSize, mp.GetMaxContentsSize()) + format.MinimumPrimeSize-requestMinSize, mp.GetMaxContentsSize()) } } // Error path: supplied bytes are smaller than payload contents. -func TestTransmitMessagePayload_SetContents(t *testing.T) { - expectedErr := "max size of message content" +func TestRequestPayload_SetContents(t *testing.T) { + prng := rand.New(rand.NewSource(42)) + payloadSize := format.MinimumPrimeSize + payload := make([]byte, payloadSize-requestMinSize) + _, err := prng.Read(payload) + if err != nil { + t.Errorf("Failed to read to payload: %+v", err) + } + mp := NewRequestPayload(payloadSize, payload, 5) + contents := make([]byte, format.MinimumPrimeSize+1) + expectedErr := fmt.Sprintf( + errReqPayloadContentsSize, len(contents), len(mp.contents)) + defer func() { - if r := recover(); r == nil || !strings.Contains(r.(string), expectedErr) { + if r := recover(); r == nil || r != expectedErr { t.Errorf("SetContents did not panic with the expected error when "+ "the size of the supplied bytes is not the same as the "+ "payload content size.\nexpected: %s\nreceived: %+v", @@ -431,16 +452,7 @@ func TestTransmitMessagePayload_SetContents(t *testing.T) { } }() - prng := rand.New(rand.NewSource(42)) - payloadSize := format.MinimumPrimeSize - payload := make([]byte, payloadSize-transmitPlMinSize) - _, err := prng.Read(payload) - if err != nil { - t.Errorf("Failed to read to payload: %+v", err) - } - - mp := NewRequestPayload(payloadSize, payload, 5) - mp.SetContents(make([]byte, format.MinimumPrimeSize+1)) + mp.SetContents(contents) } func getGroup() *cyclic.Group { diff --git a/single/message/response.go b/single/message/responsePart.go similarity index 57% rename from single/message/response.go rename to single/message/responsePart.go index 55651e50d6f2d08e9b920d149563e6d30651201c..02b80cb95fc96f2ab80be204e83ea8bbf0bb233d 100644 --- a/single/message/response.go +++ b/single/message/responsePart.go @@ -13,21 +13,41 @@ import ( jww "github.com/spf13/jwalterweatherman" ) +// Error messages. const ( - partNumLen = 1 - maxPartsLen = 1 - responseMinSize = receptionMessageVersionLen + partNumLen + maxPartsLen + sizeSize - receptionMessageVersion = 0 - receptionMessageVersionLen = 1 + // NewResponsePart + errResPartPayloadSize = "[SU] Failed to create new single-use response " + + "message part: external payload size (%d) is smaller than the " + + "minimum message size for a response part (%d)." + + // UnmarshalResponsePart + errResPartDataSize = "size of data (%d) must be at least %d" + + // ResponsePart.SetContents + errResPartContentsSize = "[SU] Failed to set contents of single-use " + + "response message part: size of the supplied contents (%d) is larger " + + "than the max message size (%d)." ) +// Sizes of fields. +const ( + resPartVersionLen = 1 + resPartPartNumLen = 1 + resPartMaxPartsLen = 1 + resPartSizeLen = 2 + resPartMinSize = resPartVersionLen + resPartPartNumLen + resPartMaxPartsLen + resPartSizeLen +) + +// The version number for the ResponsePart message. +const responsePartVersion = 0 + /* -+-----------------------------------------------------------+ -| CMIX Message Contents | -+---------+------------------+---------+---------+----------+ -| version | maxResponseParts | size | partNum | contents | -| 1 bytes | 1 byte | 2 bytes | 1 bytes | variable | -+---------+------------------+---------+---------+----------+ ++---------------------------------------------------+ +| cMix Message Contents | ++---------+---------+----------+---------+----------+ +| version | partNum | maxParts | size | contents | +| 1 byte | 1 byte | 1 byte | 2 bytes | variable | ++---------+---------+----------+---------+----------+ */ type ResponsePart struct { @@ -41,15 +61,13 @@ type ResponsePart struct { // NewResponsePart generates a new response message part of the specified size. func NewResponsePart(externalPayloadSize int) ResponsePart { - if externalPayloadSize < responseMinSize { - 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, responseMinSize) + if externalPayloadSize < resPartMinSize { + jww.FATAL.Panicf( + errResPartPayloadSize, externalPayloadSize, resPartMinSize) } rmp := mapResponsePart(make([]byte, externalPayloadSize)) - rmp.version[0] = receptionMessageVersion + rmp.version[0] = responsePartVersion return rmp } @@ -58,22 +76,21 @@ func NewResponsePart(externalPayloadSize int) ResponsePart { func mapResponsePart(data []byte) ResponsePart { return ResponsePart{ data: data, - version: data[:receptionMessageVersionLen], - partNum: data[receptionMessageVersionLen : receptionMessageVersionLen+partNumLen], - maxParts: data[receptionMessageVersionLen+partNumLen : receptionMessageVersionLen+maxPartsLen+partNumLen], - size: data[receptionMessageVersionLen+maxPartsLen+partNumLen : responseMinSize], - contents: data[responseMinSize:], + version: data[:resPartVersionLen], + partNum: data[resPartVersionLen : resPartVersionLen+resPartPartNumLen], + maxParts: data[resPartVersionLen+resPartPartNumLen : resPartVersionLen+resPartMaxPartsLen+resPartPartNumLen], + size: data[resPartVersionLen+resPartMaxPartsLen+resPartPartNumLen : resPartMinSize], + contents: data[resPartMinSize:], } } -// UnmarshalResponse converts a byte buffer into a response message part. -func UnmarshalResponse(b []byte) (ResponsePart, error) { - if len(b) < responseMinSize { - return ResponsePart{}, errors.Errorf("Size of passed in bytes "+ - "(%d) is too small to contain the message part number and max "+ - "parts (%d).", len(b), responseMinSize) +// UnmarshalResponsePart converts a byte buffer into a response message part. +func UnmarshalResponsePart(data []byte) (ResponsePart, error) { + if len(data) < resPartMinSize { + return ResponsePart{}, errors.Errorf( + errResPartDataSize, len(data), resPartMinSize) } - return mapResponsePart(b), nil + return mapResponsePart(data), nil } // Marshal returns the bytes of the message part. @@ -120,10 +137,7 @@ func (m ResponsePart) GetMaxContentsSize() int { // contents. func (m ResponsePart) SetContents(contents []byte) { if len(contents) > len(m.contents) { - jww.FATAL.Panicf("Failed to set contents of single-use response "+ - "message part: max size of message contents (%d) is smaller than "+ - "the size of the supplied contents (%d).", - len(m.contents), len(contents)) + jww.FATAL.Panicf(errResPartContentsSize, len(contents), len(m.contents)) } binary.BigEndian.PutUint16(m.size, uint16(len(contents))) diff --git a/single/message/response_test.go b/single/message/responsePart_test.go similarity index 70% rename from single/message/response_test.go rename to single/message/responsePart_test.go index 13f1c1e2367476795a747de2398a23e4e7c67c9e..f8a315c13f66d0f7a261844f4593d73d98ee8cf3 100644 --- a/single/message/response_test.go +++ b/single/message/responsePart_test.go @@ -9,23 +9,23 @@ package message import ( "bytes" + "fmt" "math/rand" "reflect" - "strings" "testing" ) // Happy path. -func Test_newResponseMessagePart(t *testing.T) { +func Test_NewResponsePart(t *testing.T) { prng := rand.New(rand.NewSource(42)) payloadSize := prng.Intn(2000) expected := ResponsePart{ data: make([]byte, payloadSize), - version: make([]byte, receptionMessageVersionLen), - partNum: make([]byte, partNumLen), - maxParts: make([]byte, maxPartsLen), - size: make([]byte, sizeSize), - contents: make([]byte, payloadSize-partNumLen-maxPartsLen-sizeSize-receptionMessageVersionLen), + version: make([]byte, resPartVersionLen), + partNum: make([]byte, resPartPartNumLen), + maxParts: make([]byte, resPartMaxPartsLen), + size: make([]byte, resPartSizeLen), + contents: make([]byte, payloadSize-resPartMinSize), } rmp := NewResponsePart(payloadSize) @@ -37,21 +37,23 @@ func Test_newResponseMessagePart(t *testing.T) { } // Error path: provided contents size is not large enough. -func Test_newResponseMessagePart_PayloadSizeError(t *testing.T) { - expectedErr := "size of external payload" +func Test_NewResponsePart_PayloadSizeError(t *testing.T) { + externalPayloadSize := 1 + expectedErr := fmt.Sprintf( + errResPartPayloadSize, externalPayloadSize, resPartMinSize) defer func() { - if r := recover(); r == nil || !strings.Contains(r.(string), expectedErr) { + if r := recover(); r == nil || r != expectedErr { t.Errorf("NewResponsePart did not panic with the expected error "+ "when the size of the payload is smaller than the required "+ "size.\nexpected: %s\nreceived: %+v", expectedErr, r) } }() - _ = NewResponsePart(1) + _ = NewResponsePart(externalPayloadSize) } // Happy path. -func Test_mapResponseMessagePart(t *testing.T) { +func Test_mapResponsePart(t *testing.T) { prng := rand.New(rand.NewSource(42)) expectedVersion := uint8(0) expectedPartNum := uint8(prng.Uint32()) @@ -88,7 +90,7 @@ func Test_mapResponseMessagePart(t *testing.T) { } // Happy path. -func TestResponseMessagePart_Marshal_Unmarshal(t *testing.T) { +func TestResponsePart_Marshal_UnmarshalResponsePart(t *testing.T) { prng := rand.New(rand.NewSource(42)) payload := make([]byte, prng.Intn(2000)) prng.Read(payload) @@ -96,9 +98,9 @@ func TestResponseMessagePart_Marshal_Unmarshal(t *testing.T) { data := rmp.Marshal() - newRmp, err := UnmarshalResponse(data) + newRmp, err := UnmarshalResponsePart(data) if err != nil { - t.Errorf("UnmarshalResponse produced an error: %+v", err) + t.Errorf("UnmarshalResponsePart produced an error: %+v", err) } if !reflect.DeepEqual(rmp, newRmp) { @@ -108,16 +110,19 @@ func TestResponseMessagePart_Marshal_Unmarshal(t *testing.T) { } // Error path: provided bytes are too small. -func Test_unmarshalResponseMessage(t *testing.T) { - _, err := UnmarshalResponse([]byte{1}) - if err == nil { - t.Error("UnmarshalResponse did not produce an error when the " + - "byte slice is smaller required.") +func Test_UnmarshalResponsePart_Error(t *testing.T) { + data := []byte{1} + expectedErr := fmt.Sprintf(errResPartDataSize, len(data), resPartMinSize) + _, err := UnmarshalResponsePart([]byte{1}) + if err == nil || err.Error() != expectedErr { + t.Errorf("UnmarshalResponsePart did not produce the expected error "+ + "when the byte slice is smaller required."+ + "\nexpected: %s\nreceived: %+v", expectedErr, err) } } // Happy path. -func TestResponseMessagePart_SetPartNum_GetPartNum(t *testing.T) { +func TestResponsePart_SetPartNum_GetPartNum(t *testing.T) { prng := rand.New(rand.NewSource(42)) expectedPartNum := uint8(prng.Uint32()) rmp := NewResponsePart(prng.Intn(2000)) @@ -131,7 +136,7 @@ func TestResponseMessagePart_SetPartNum_GetPartNum(t *testing.T) { } // Happy path. -func TestResponseMessagePart_SetMaxParts_GetMaxParts(t *testing.T) { +func TestResponsePart_SetMaxParts_GetMaxParts(t *testing.T) { prng := rand.New(rand.NewSource(42)) expectedMaxParts := uint8(prng.Uint32()) rmp := NewResponsePart(prng.Intn(2000)) @@ -145,10 +150,10 @@ func TestResponseMessagePart_SetMaxParts_GetMaxParts(t *testing.T) { } // Happy path. -func TestResponseMessagePart_SetContents_GetContents_GetContentsSize_GetMaxContentsSize(t *testing.T) { +func TestResponsePart_SetContents_GetContents_GetContentsSize_GetMaxContentsSize(t *testing.T) { prng := rand.New(rand.NewSource(42)) externalPayloadSize := prng.Intn(2000) - contentSize := externalPayloadSize - responseMinSize - 10 + contentSize := externalPayloadSize - resPartMinSize - 10 expectedContents := make([]byte, contentSize) prng.Read(expectedContents) rmp := NewResponsePart(externalPayloadSize) @@ -164,24 +169,26 @@ func TestResponseMessagePart_SetContents_GetContents_GetContentsSize_GetMaxConte "\nexpected: %d\nrecieved: %d", contentSize, rmp.GetContentsSize()) } - if externalPayloadSize-responseMinSize != rmp.GetMaxContentsSize() { + if externalPayloadSize-resPartMinSize != rmp.GetMaxContentsSize() { t.Errorf("GetMaxContentsSize failed to return the expected max "+ "contents size.\nexpected: %d\nrecieved: %d", - externalPayloadSize-responseMinSize, rmp.GetMaxContentsSize()) + externalPayloadSize-resPartMinSize, rmp.GetMaxContentsSize()) } } // Error path: size of supplied contents does not match message contents size. -func TestResponseMessagePart_SetContents_ContentsSizeError(t *testing.T) { - expectedErr := "max size of message contents" +func TestResponsePart_SetContents_ContentsSizeError(t *testing.T) { + payloadSize, contentsLen := 255, 500 + expectedErr := fmt.Sprintf( + errResPartContentsSize, contentsLen, payloadSize-resPartMinSize) defer func() { - if r := recover(); r == nil || !strings.Contains(r.(string), expectedErr) { + if r := recover(); r == nil || r != expectedErr { t.Errorf("SetContents did not panic with the expected error when "+ "the size of the supplied bytes is larger than the content "+ "size.\nexpected: %s\nreceived: %+v", expectedErr, r) } }() - rmp := NewResponsePart(255) - rmp.SetContents(make([]byte, 500)) + rmp := NewResponsePart(payloadSize) + rmp.SetContents(make([]byte, contentsLen)) } diff --git a/single/request.go b/single/request.go index 0db4f433469e5e5a4b8b5358da8de139b8c3a130..2fa534197e5735ce631cc43fb9a77c173f6b66ae 100644 --- a/single/request.go +++ b/single/request.go @@ -41,8 +41,28 @@ func GetDefaultRequestParams() RequestParams { } } -// GetMaxRequestSize returns the max size of a request payload. -func GetMaxRequestSize(net cmix.Client, e2eGrp *cyclic.Group) uint { +// Error messages. +const ( + // TransmitRequest + errNetworkHealth = "cannot send singe-use request when the network is not healthy" + errMakeDhKeys = "failed to generate DH keys (%s for %s): %+v" + errMakeIDs = "failed to generate IDs (%s for %s): %+v" + errAddFingerprint = "failed to add fingerprint %d of %d: %+v (%s for %s)" + errSendRequest = "failed to send %s request to %s: %+v" + + // generateDhKeys + errGenerateInGroup = "failed to generate private key in group: %+v" + + // makeIDs + errMakeNonce = "failed to generate nonce: %+v" + errNewEphemeralID = "failed to generate address ID from newly generated ID: %+v" + + // waitForTimeout + errResponseTimeout = "waiting for response to single-use request timed out after %s" +) + +// GetMaxRequestSize returns the maximum size of a request payload. +func GetMaxRequestSize(net cmix.Client, e2eGrp *cyclic.Group) int { payloadSize := message.GetRequestPayloadSize(net.GetMaxMessageLength(), e2eGrp.GetP().ByteLen()) return message.GetRequestContentsSize(payloadSize) @@ -70,8 +90,7 @@ func TransmitRequest(recipient contact.Contact, tag string, payload []byte, callback Response, param RequestParams, net cmix.Client, rng csprng.Source, e2eGrp *cyclic.Group) (id.Round, receptionID.EphemeralIdentity, error) { if !net.IsHealthy() { - return 0, receptionID.EphemeralIdentity{}, errors.New("Cannot " + - "send singe use when network is not healthy") + return 0, receptionID.EphemeralIdentity{}, errors.New(errNetworkHealth) } // Get address ID address space size; this blocks until the address space @@ -82,7 +101,8 @@ func TransmitRequest(recipient contact.Contact, tag string, payload []byte, // Generate DH key and public key dhKey, publicKey, err := generateDhKeys(e2eGrp, recipient.DhPubKey, rng) if err != nil { - return 0, receptionID.EphemeralIdentity{}, err + return 0, receptionID.EphemeralIdentity{}, + errors.Errorf(errMakeDhKeys, tag, recipient, err) } // Build the message payload @@ -97,25 +117,23 @@ func TransmitRequest(recipient contact.Contact, tag string, payload []byte, requestPayload, publicKey, addressSize, param.Timeout, timeStart, rng) if err != nil { return 0, receptionID.EphemeralIdentity{}, - errors.Errorf("failed to generate IDs: %+v", err) + errors.Errorf(errMakeIDs, tag, recipient, err) } - // Encrypt payload + // Encrypt and assemble payload fp := singleUse.NewTransmitFingerprint(recipient.DhPubKey) key := singleUse.NewTransmitKey(dhKey) encryptedPayload := auth.Crypt(key, fp[:24], requestPayload.Marshal()) - - // Generate CMIX message MAC - mac := singleUse.MakeMAC(key, encryptedPayload) - - // Assemble the payload request.SetPubKey(publicKey) request.SetPayload(encryptedPayload) + // Generate cMix message MAC + mac := singleUse.MakeMAC(key, encryptedPayload) + // Register the response pickup collator := message.NewCollator(param.MaxResponseMessages) timeoutKillChan := make(chan bool) - callbackOnce := sync.Once{} + var callbackOnce sync.Once wrapper := func(payload []byte, receptionID receptionID.EphemeralIdentity, round rounds.Round, err error) { select { @@ -131,12 +149,12 @@ func TransmitRequest(recipient contact.Contact, tag string, payload []byte, cyphers := makeCyphers(dhKey, param.MaxResponseMessages) - for i := uint8(0); i < param.MaxResponseMessages; i++ { + for i, cy := range cyphers { processor := responseProcessor{ sendingID: sendingID, c: collator, callback: wrapper, - cy: cyphers[i], + cy: cy, tag: tag, recipient: &recipient, } @@ -144,8 +162,8 @@ func TransmitRequest(recipient contact.Contact, tag string, payload []byte, err = net.AddFingerprint( sendingID.Source, processor.cy.GetFingerprint(), &processor) if err != nil { - return 0, receptionID.EphemeralIdentity{}, - errors.Errorf("failed to add fingerprint %d IDs: %+v", i, err) + return 0, receptionID.EphemeralIdentity{}, errors.Errorf( + errAddFingerprint, i, len(cyphers), tag, recipient, err) } } @@ -161,9 +179,9 @@ func TransmitRequest(recipient contact.Contact, tag string, payload []byte, rid, _, err := net.Send(recipient.ID, cmixMsg.RandomFingerprint(rng), svc, request.Marshal(), mac, param.CmixParam) - if err != nil { - return 0, receptionID.EphemeralIdentity{}, err + return 0, receptionID.EphemeralIdentity{}, + errors.Errorf(errSendRequest, tag, recipient, err) } remainingTimeout := param.Timeout - netTime.Since(timeStart) @@ -173,20 +191,21 @@ func TransmitRequest(recipient contact.Contact, tag string, payload []byte, } // generateDhKeys generates a new public key and DH key. -func generateDhKeys(grp *cyclic.Group, dhPubKey *cyclic.Int, - rng io.Reader) (*cyclic.Int, *cyclic.Int, error) { +func generateDhKeys(grp *cyclic.Group, dhPubKey *cyclic.Int, rng io.Reader) ( + dhKey, publicKey *cyclic.Int, err error) { + // Generate private key privKeyBytes, err := csprng.GenerateInGroup( grp.GetP().Bytes(), grp.GetP().ByteLen(), rng) if err != nil { - return nil, nil, errors.Errorf("failed to generate key in group: %+v", - err) + return nil, nil, errors.Errorf(errGenerateInGroup, err) } privKey := grp.NewIntFromBytes(privKeyBytes) // Generate public key and DH key - publicKey := grp.ExpG(privKey, grp.NewInt(1)) - dhKey := grp.Exp(dhPubKey, privKey, grp.NewInt(1)) + publicKey = grp.ExpG(privKey, grp.NewInt(1)) + dhKey = grp.Exp(dhPubKey, privKey, grp.NewInt(1)) + return dhKey, publicKey, nil } @@ -195,7 +214,7 @@ func generateDhKeys(grp *cyclic.Group, dhPubKey *cyclic.Int, // contains a nonce. If the generated address ID has a window that is not within // +/- the given 2*Timeout from now, then the IDs are generated again using a // new nonce. -func makeIDs(msg message.RequestPayload, publicKey *cyclic.Int, +func makeIDs(payload message.RequestPayload, publicKey *cyclic.Int, addressSize uint8, timeout time.Duration, timeNow time.Time, rng io.Reader) ( message.RequestPayload, receptionID.EphemeralIdentity, error) { var rid *id.ID @@ -208,32 +227,29 @@ func makeIDs(msg message.RequestPayload, publicKey *cyclic.Int, // Loop until the address ID's start and end are within bounds for windowStart.Before(start) || windowEnd.After(end) { // Generate new nonce - err := msg.SetNonce(rng) + err := payload.SetNonce(rng) if err != nil { return message.RequestPayload{}, receptionID.EphemeralIdentity{}, - errors.Errorf("failed to generate nonce: %+v", err) + errors.Errorf(errMakeNonce, err) } // Generate ID from unencrypted payload - rid = msg.GetRID(publicKey) + rid = payload.GetRecipientID(publicKey) // Generate the address ID ephID, start, end, err = ephemeral.GetId( rid, uint(addressSize), timeNow.UnixNano()) if err != nil { return message.RequestPayload{}, receptionID.EphemeralIdentity{}, - errors.Errorf("failed to generate address ID from newly "+ - "generated ID: %+v", err) + errors.Errorf(errNewEphemeralID, err) } - jww.DEBUG.Printf("address.GetId(%s, %d, %d) = %d", rid, - addressSize, timeNow.UnixNano(), ephID.Int64()) } - jww.INFO.Printf("generated by singe use sender reception ID for single "+ - "use: %s, ephId: %d, pubkey: %x, msg: %s", - rid, ephID.Int64(), publicKey.Bytes(), msg) + jww.INFO.Printf("[SU] Generated singe-use sender reception ID: %s, "+ + "ephId: %d, publicKey: %x, payload: %q", + rid, ephID.Int64(), publicKey.Bytes(), payload) - return msg, receptionID.EphemeralIdentity{ + return payload, receptionID.EphemeralIdentity{ EphId: ephID, Source: rid, }, nil @@ -241,14 +257,16 @@ func makeIDs(msg message.RequestPayload, publicKey *cyclic.Int, // waitForTimeout is a long-running thread which handles timing out a request. // It can be canceled by channel. -func waitForTimeout(kill chan bool, callback callbackWrapper, timeout time.Duration) { - timer := time.NewTimer(timeout) +func waitForTimeout(kill chan bool, cb callbackWrapper, timeout time.Duration) { select { case <-kill: return - case <-timer.C: - err := errors.Errorf("waiting for response to single-use transmission "+ - "timed out after %s.", timeout) - callback(nil, receptionID.EphemeralIdentity{}, rounds.Round{}, err) + case <-time.After(timeout): + cb( + nil, + receptionID.EphemeralIdentity{}, + rounds.Round{}, + errors.Errorf(errResponseTimeout, timeout), + ) } } diff --git a/single/responseProcessor.go b/single/responseProcessor.go index 33b02298df7a82c55a5446208db5b26c2b2460b4..a6f0d080abddfbb2e67f9232c18eafc99c8eb2ee 100644 --- a/single/responseProcessor.go +++ b/single/responseProcessor.go @@ -26,20 +26,21 @@ type responseProcessor struct { // Process decrypts a response part and adds it to the collator - returning // a full response to the callback when all parts are received. func (rsp *responseProcessor) Process(ecrMsg format.Message, - receptionID receptionID.EphemeralIdentity, - round rounds.Round) { + receptionID receptionID.EphemeralIdentity, round rounds.Round) { decrypted, err := rsp.cy.Decrypt(ecrMsg.GetContents(), ecrMsg.GetMac()) if err != nil { - jww.ERROR.Printf("Failed to decrypt payload for %s to %s, "+ - "single use may fail: %+v", rsp.tag, rsp.recipient.ID, err) + jww.ERROR.Printf("[SU] Failed to decrypt single-use response "+ + "payload for %s to %s: %+v", + rsp.tag, rsp.recipient.ID, err) return } payload, done, err := rsp.c.Collate(decrypted) if err != nil { - jww.ERROR.Printf("Failed to collage payload for %s to %s, "+ - "single use may fail: %+v", rsp.tag, rsp.recipient.ID, err) + jww.ERROR.Printf("[SU] Failed to collate single-use response payload "+ + "for %s to %s, single use may fail: %+v", + rsp.tag, rsp.recipient.ID, err) return }