go-peer/pkg/client/client_test.go
2024-05-29 10:34:13 +07:00

603 lines
13 KiB
Go

// nolint: goerr113
package client
import (
"bytes"
"fmt"
"testing"
"github.com/number571/go-peer/pkg/client/message"
"github.com/number571/go-peer/pkg/crypto/asymmetric"
"github.com/number571/go-peer/pkg/crypto/hashing"
"github.com/number571/go-peer/pkg/crypto/random"
"github.com/number571/go-peer/pkg/crypto/symmetric"
"github.com/number571/go-peer/pkg/encoding"
"github.com/number571/go-peer/pkg/payload"
"github.com/number571/go-peer/pkg/payload/joiner"
"github.com/number571/go-peer/pkg/utils"
testutils "github.com/number571/go-peer/test/utils"
)
const (
tcPubKey1023Bit = "PubKey{30818802818067D7C7F48CCCF318A504721D8521ED04DFD24DE947D50476212E279DADA7F627102D6140B922BC30E778BE7FD120E73D02C407E4D053D55C8F3CFACED11CCB8DC96539F51E34B32544EAB8812129BE7AD107E9A352E70F912962D6D0BABAB629F20332A3FBA66BC28D2944F44859959788428300F58DA0873A6796073B17CEEB0203010001}"
tcMessageSize = (2 << 10)
tcKeySizeBits = 1024
)
var (
tgMsgLimit = testNewClient().GetMessageLimit()
tgPrivKey = asymmetric.LoadRSAPrivKey(testutils.Tc1PrivKey1024)
tgMessages = []string{
testutils.TcBody,
"",
"A",
"AA",
"AAA",
"AAAA",
"AAAAA",
"AAAAAA",
"AAAAAAA",
"AAAAAAAA",
"AAAAAAAAA",
"AAAAAAAAAA",
"AAAAAAAAAAA",
"AAAAAAAAAAAA",
"AAAAAAAAAAAAA",
"AAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
random.NewCSPRNG().GetString(tgMsgLimit - encoding.CSizeUint64), // maximum size of message - payload64.head
}
)
func TestError(t *testing.T) {
t.Parallel()
str := "value"
err := &SClientError{str}
if err.Error() != errPrefix+str {
t.Error("incorrect err.Error()")
return
}
}
func TestClientPanicWithMessageSize(t *testing.T) {
t.Parallel()
defer func() {
if r := recover(); r == nil {
t.Error("nothing panics")
return
}
}()
_ = NewClient(
message.NewSettings(&message.SSettings{
FMessageSizeBytes: 1024,
FKeySizeBits: 512,
}),
tgPrivKey,
)
}
func TestClientPanicWithKeySize(t *testing.T) {
t.Parallel()
testDiffKeySize(t)
testLittleKeySize(t)
testLittleMessageSize(t)
}
func testDiffKeySize(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Error("nothing panics")
return
}
}()
_ = NewClient(
message.NewSettings(&message.SSettings{
FMessageSizeBytes: tcMessageSize,
FKeySizeBits: 4096,
}),
tgPrivKey,
)
}
func testLittleKeySize(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Error("nothing panics")
return
}
}()
_ = NewClient(
message.NewSettings(&message.SSettings{
FMessageSizeBytes: tcMessageSize,
FKeySizeBits: 128,
}),
asymmetric.NewRSAPrivKey(128),
)
}
func testLittleMessageSize(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Error("nothing panics")
return
}
}()
_ = NewClient(
message.NewSettings(&message.SSettings{
FMessageSizeBytes: 128,
FKeySizeBits: tcKeySizeBits,
}),
tgPrivKey,
)
}
func TestEncrypt(t *testing.T) {
t.Parallel()
client1 := testNewClient()
client2 := testNewClient()
_ = client1.GetSettings()
_ = client1.GetPrivKey()
pl := payload.NewPayload64(uint64(testutils.TcHead), []byte(testutils.TcBody))
msg, err := client1.EncryptMessage(client2.GetPubKey(), pl.ToBytes())
if err != nil {
t.Error(err)
return
}
// os.WriteFile("test_binary.msg", msg.ToBytes(), 0644)
// os.WriteFile("test_string.msg", []byte(msg.ToString()), 0644)
_, decMsg, err := client2.DecryptMessage(msg)
if err != nil {
t.Error(err)
return
}
decPl := payload.LoadPayload64(decMsg)
if !bytes.Equal([]byte(testutils.TcBody), decPl.GetBody()) {
t.Error("data not equal with decrypted data")
return
}
}
func TestDecrypt(t *testing.T) {
t.Parallel()
client1 := testNewClient()
pl := payload.NewPayload64(uint64(testutils.TcHead), []byte(testutils.TcBody))
msg1, err := client1.EncryptMessage(client1.GetPubKey(), pl.ToBytes())
if err != nil {
t.Error(err)
return
}
if _, _, err := client1.DecryptMessage(msg1); err != nil {
t.Error(err)
return
}
msg, err := message.LoadMessage(client1.GetSettings(), msg1)
if err != nil {
t.Error(err)
return
}
newEncd1 := make([]byte, len(msg.GetEncd()))
copy(newEncd1, msg.GetEncd())
newEncd1[0] ^= 1
newMsg1 := message.NewMessage(msg.GetEnck(), newEncd1)
if _, _, err := client1.DecryptMessage(newMsg1.ToBytes()); err == nil {
t.Error("success decrypt invalid message")
return
}
newEncd2 := make([]byte, len(msg.GetEncd()))
copy(newEncd2, msg.GetEncd())
newEncd2[symmetric.CAESBlockSize+8+1] ^= 1 // public key padding
newMsg2 := message.NewMessage(msg.GetEnck(), newEncd2)
if _, _, err := client1.DecryptMessage(newMsg2.ToBytes()); err == nil {
t.Error("success decrypt invalid message (public key)")
return
}
newEncd3 := make([]byte, len(msg.GetEncd()))
copy(newEncd3, msg.GetEncd())
newEncd3[symmetric.CAESBlockSize+196+1] ^= 1 // hash padding
newMsg3 := message.NewMessage(msg.GetEnck(), newEncd3)
if _, _, err := client1.DecryptMessage(newMsg3.ToBytes()); err == nil {
t.Error("success decrypt invalid message (hash)")
return
}
newEncd4 := make([]byte, len(msg.GetEncd()))
copy(newEncd4, msg.GetEncd())
newEncd4[symmetric.CAESBlockSize+236+1] ^= 1 // sign padding
newMsg4 := message.NewMessage(msg.GetEnck(), newEncd4)
if _, _, err := client1.DecryptMessage(newMsg4.ToBytes()); err == nil {
t.Error("success decrypt invalid message (sign)")
return
}
if _, _, err := client1.DecryptMessage(nil); err == nil {
t.Error("success decrypt nil message")
return
}
client1Ptr := client1.(*sClient)
msg3, err := client1Ptr.tInvalidEncryptPayload1(client1.GetPubKey(), pl)
if err != nil {
t.Error(err)
return
}
if _, _, err := client1.DecryptMessage(msg3.ToBytes()); err == nil {
t.Error("success decrypt message with incorrect payload (1)")
return
}
msg4, err := client1Ptr.tInvalidEncryptPayload2(client1.GetPubKey(), pl)
if err != nil {
t.Error(err)
return
}
if _, _, err := client1.DecryptMessage(msg4.ToBytes()); err == nil {
t.Error("success decrypt message with incorrect payload (2)")
return
}
msg5, err := client1Ptr.tInvalidEncryptPayload3(client1.GetPubKey(), pl)
if err != nil {
t.Error(err)
return
}
if _, _, err := client1.DecryptMessage(msg5.ToBytes()); err == nil {
t.Error("success decrypt message with incorrect payload (3)")
return
}
msg6, err := client1Ptr.tInvalidEncryptPayload4(client1.GetPubKey(), pl)
if err != nil {
t.Error(err)
return
}
if _, _, err := client1.DecryptMessage(msg6.ToBytes()); err == nil {
t.Error("success decrypt message with incorrect payload (4)")
return
}
}
func TestMessageSize(t *testing.T) {
t.Parallel()
client1 := testNewClient()
for _, smsg := range tgMessages {
pl := payload.NewPayload64(uint64(testutils.TcHead), []byte(smsg))
msg, err := client1.EncryptMessage(client1.GetPubKey(), pl.ToBytes())
if err != nil {
t.Error(err)
return
}
if uint64(len(msg)) != client1.GetSettings().GetMessageSizeBytes() {
t.Error("got invalid message size bytes")
return
}
}
}
func TestGetMessageLimit(t *testing.T) {
t.Parallel()
client1 := testNewClient()
msg1 := random.NewCSPRNG().GetBytes(tgMsgLimit - encoding.CSizeUint64)
pld1 := payload.NewPayload64(uint64(testutils.TcHead), msg1)
if _, err := client1.EncryptMessage(client1.GetPubKey(), pld1.ToBytes()); err != nil {
t.Error("message1 > message limit:", err)
return
}
msg2 := random.NewCSPRNG().GetBytes(tgMsgLimit + 1)
pld2 := payload.NewPayload64(uint64(testutils.TcHead), msg2)
if _, err := client1.EncryptMessage(client1.GetPubKey(), pld2.ToBytes()); err == nil {
t.Error("message2 > message limit but not alert:", err)
return
}
}
func testNewClient() IClient {
return NewClient(
message.NewSettings(&message.SSettings{
FMessageSizeBytes: tcMessageSize,
FKeySizeBits: tcKeySizeBits,
}),
tgPrivKey,
)
}
// INVALID_1
func (p *sClient) tInvalidEncryptPayload1(pRecv asymmetric.IPubKey, pPld payload.IPayload64) (message.IMessage, error) {
var (
msgLimitSize = p.GetMessageLimit()
resultSize = uint64(len(pPld.ToBytes()))
)
if resultSize > msgLimitSize {
return nil, utils.MergeErrors(
ErrLimitMessageSize,
fmt.Errorf(
"limit of message size without hex encoding = %d bytes < current payload size with additional padding = %d bytes",
msgLimitSize,
resultSize,
),
)
}
return p.tInvalidEncryptWithParams1(
pRecv,
pPld,
msgLimitSize-resultSize,
), nil
}
func (p *sClient) tInvalidEncryptWithParams1(pRecv asymmetric.IPubKey, pPld payload.IPayload64, pPadd uint64) message.IMessage {
var (
rand = random.NewCSPRNG()
salt = rand.GetBytes(symmetric.CAESKeySize)
session = rand.GetBytes(symmetric.CAESKeySize)
)
payloadBytes := pPld.ToBytes()
doublePayload := payload.NewPayload64(
uint64(len(payloadBytes))-1,
bytes.Join(
[][]byte{
payloadBytes,
rand.GetBytes(pPadd),
},
[]byte{},
),
)
hash := hashing.NewHMACSHA256Hasher(salt, bytes.Join(
[][]byte{
p.GetPubKey().GetHasher().ToBytes(),
pRecv.GetHasher().ToBytes(),
doublePayload.ToBytes(),
},
[]byte{},
)).ToBytes()
encKey := pRecv.EncryptBytes(session)
if encKey == nil {
panic(ErrEncryptSymmetricKey)
}
cipher := symmetric.NewAESCipher(session)
return message.NewMessage(
encKey,
cipher.EncryptBytes(joiner.NewBytesJoiner32([][]byte{
p.GetPubKey().ToBytes(),
salt,
hash,
p.fPrivKey.SignBytes(hash),
doublePayload.ToBytes(),
})),
)
}
// INVALID_2
func (p *sClient) tInvalidEncryptPayload2(pRecv asymmetric.IPubKey, pPld payload.IPayload64) (message.IMessage, error) {
var (
msgLimitSize = p.GetMessageLimit()
resultSize = uint64(len(pPld.GetBody()))
)
if resultSize > msgLimitSize {
return nil, ErrLimitMessageSize
}
return p.tInvalidEncryptWithParams2(
pRecv,
pPld,
msgLimitSize-resultSize,
)
}
func (p *sClient) tInvalidEncryptWithParams2(
pRecv asymmetric.IPubKey,
pPld payload.IPayload64,
pPadd uint64,
) (message.IMessage, error) {
var (
rand = random.NewCSPRNG()
salt = rand.GetBytes(symmetric.CAESKeySize)
session = rand.GetBytes(symmetric.CAESKeySize)
)
data := joiner.NewBytesJoiner32([][]byte{
bytes.Join(
[][]byte{
pPld.ToBytes(),
rand.GetBytes(pPadd),
{1, 2, 3, 4}, // uint32
},
[]byte{},
),
})
hash := hashing.NewHMACSHA256Hasher(salt, bytes.Join(
[][]byte{
p.GetPubKey().GetHasher().ToBytes(),
pRecv.GetHasher().ToBytes(),
data,
},
[]byte{},
)).ToBytes()
encKey := pRecv.EncryptBytes(session)
if encKey == nil {
return nil, ErrEncryptSymmetricKey
}
cipher := symmetric.NewAESCipher(session)
return message.NewMessage(
encKey,
cipher.EncryptBytes(joiner.NewBytesJoiner32([][]byte{
p.GetPubKey().ToBytes(),
salt,
hash,
p.fPrivKey.SignBytes(hash),
data,
})),
), nil
}
// INVALID_3
func (p *sClient) tInvalidEncryptPayload3(pRecv asymmetric.IPubKey, pPld payload.IPayload64) (message.IMessage, error) {
var (
msgLimitSize = p.GetMessageLimit()
resultSize = uint64(len(pPld.GetBody()))
)
if resultSize > msgLimitSize {
return nil, ErrLimitMessageSize
}
return p.tInvalidEncryptWithParams3(
pRecv,
pPld,
msgLimitSize+encoding.CSizeUint64,
)
}
func (p *sClient) tInvalidEncryptWithParams3(
pRecv asymmetric.IPubKey,
_ payload.IPayload64,
pPadd uint64,
) (message.IMessage, error) {
var (
rand = random.NewCSPRNG()
salt = rand.GetBytes(symmetric.CAESKeySize)
session = rand.GetBytes(symmetric.CAESKeySize)
)
data := joiner.NewBytesJoiner32([][]byte{
nil,
rand.GetBytes(pPadd),
})
hash := hashing.NewHMACSHA256Hasher(salt, bytes.Join(
[][]byte{
p.GetPubKey().GetHasher().ToBytes(),
pRecv.GetHasher().ToBytes(),
data,
},
[]byte{},
)).ToBytes()
encKey := pRecv.EncryptBytes(session)
if encKey == nil {
return nil, ErrEncryptSymmetricKey
}
cipher := symmetric.NewAESCipher(session)
return message.NewMessage(
encKey,
cipher.EncryptBytes(joiner.NewBytesJoiner32([][]byte{
p.GetPubKey().ToBytes(),
salt,
hash,
p.fPrivKey.SignBytes(hash),
data,
})),
), nil
}
// INVALID_4
func (p *sClient) tInvalidEncryptPayload4(pRecv asymmetric.IPubKey, pPld payload.IPayload64) (message.IMessage, error) {
var (
msgLimitSize = p.GetMessageLimit()
resultSize = uint64(len(pPld.GetBody()))
)
if resultSize > msgLimitSize {
return nil, ErrLimitMessageSize
}
return p.tInvalidEncryptWithParams4(
pRecv,
pPld,
msgLimitSize-resultSize-encoding.CSizeUint64,
)
}
func (p *sClient) tInvalidEncryptWithParams4(
pRecv asymmetric.IPubKey,
pPld payload.IPayload64,
pPadd uint64,
) (message.IMessage, error) {
var (
rand = random.NewCSPRNG()
salt = rand.GetBytes(symmetric.CAESKeySize)
session = rand.GetBytes(symmetric.CAESKeySize)
)
data := joiner.NewBytesJoiner32([][]byte{
pPld.ToBytes(),
rand.GetBytes(pPadd),
})
hash := hashing.NewHMACSHA256Hasher(salt, bytes.Join(
[][]byte{
p.GetPubKey().GetHasher().ToBytes(),
pRecv.GetHasher().ToBytes(),
data,
},
[]byte{},
)).ToBytes()
hash[0] ^= 1
encKey := pRecv.EncryptBytes(session)
if encKey == nil {
return nil, ErrEncryptSymmetricKey
}
cipher := symmetric.NewAESCipher(session)
return message.NewMessage(
encKey,
cipher.EncryptBytes(joiner.NewBytesJoiner32([][]byte{
p.GetPubKey().ToBytes(),
salt,
hash,
p.fPrivKey.SignBytes(hash),
data,
})),
), nil
}