From 17a88925ff47353c9db2da8d14d940c260a434c8 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Fri, 1 Sep 2023 17:08:07 +0200 Subject: [PATCH 01/30] New crypto wip --- utils/crypto.go | 35 ++++++--- utils/crypto_header.go | 96 +++++++++++++++++++++++ utils/new_crypto.go | 160 +++++++++++++++++++++++++++++++++++++++ utils/new_crypto_test.go | 28 +++++++ 4 files changed, 309 insertions(+), 10 deletions(-) create mode 100644 utils/crypto_header.go create mode 100644 utils/new_crypto.go create mode 100644 utils/new_crypto_test.go diff --git a/utils/crypto.go b/utils/crypto.go index 1fef1b13..c28e8cc4 100644 --- a/utils/crypto.go +++ b/utils/crypto.go @@ -29,10 +29,14 @@ var valIV = "0123456789012345" // // returns the base64 encoded encrypted string. func EncryptString(cipherKey string, message string, useRandomInitializationVector bool) string { - block, _ := aesCipher(cipherKey) + block, _ := legacyAesCipher(cipherKey) message = encodeNonASCIIChars(message) - value := []byte(message) + + return base64.StdEncoding.EncodeToString(encrypt(block, []byte(message), useRandomInitializationVector)) +} + +func encrypt(block cipher.Block, value []byte, useRandomInitializationVector bool) []byte { value = padWithPKCS7(value) iv := make([]byte, aes.BlockSize) if useRandomInitializationVector { @@ -45,9 +49,9 @@ func EncryptString(cipherKey string, message string, useRandomInitializationVect cipherBytes := make([]byte, len(value)) blockmode.CryptBlocks(cipherBytes, value) if useRandomInitializationVector { - return base64.StdEncoding.EncodeToString(append(iv, cipherBytes...)) + return append(iv, cipherBytes...) } - return base64.StdEncoding.EncodeToString(cipherBytes) + return cipherBytes } type A struct { @@ -73,7 +77,7 @@ func DecryptString(cipherKey string, message string, useRandomInitializationVect return "**decrypt error***", errors.New("message is empty") } - block, aesErr := aesCipher(cipherKey) + block, aesErr := legacyAesCipher(cipherKey) if aesErr != nil { return "***decrypt error***", fmt.Errorf("decrypt error aes cipher: %s", aesErr) } @@ -82,6 +86,17 @@ func DecryptString(cipherKey string, message string, useRandomInitializationVect if decodeErr != nil { return "***decrypt error***", fmt.Errorf("decrypt error on decode: %s", decodeErr) } + + val, e := decrypt(block, value, useRandomInitializationVector) + + if e != nil { + return "***decrypt error***", fmt.Errorf("decrypt error: %s", e) + } + + return fmt.Sprintf("%s", string(val)), nil +} + +func decrypt(block cipher.Block, value []byte, useRandomInitializationVector bool) (retVal []byte, err error) { iv := make([]byte, aes.BlockSize) if useRandomInitializationVector { iv = value[:16] @@ -94,27 +109,27 @@ func DecryptString(cipherKey string, message string, useRandomInitializationVect //to handle decryption errors defer func() { if r := recover(); r != nil { - retVal, err = "***decrypt error***", fmt.Errorf("decrypt error: %s", r) + retVal, err = nil, fmt.Errorf("decrypt error: %s", r) } }() decrypted := make([]byte, len(value)) decrypter.CryptBlocks(decrypted, value) val, err := unpadPKCS7(decrypted) if err != nil { - return "***decrypt error***", fmt.Errorf("decrypt error: %s", err) + return nil, fmt.Errorf("decrypt error: %s", err) } - return fmt.Sprintf("%s", string(val)), nil + return val, nil } -// aesCipher returns the cipher block +// legacyAesCipher returns the cipher block // // It accepts the following parameters: // cipherKey: cipher key. // // returns the cipher block, // error if any. -func aesCipher(cipherKey string) (cipher.Block, error) { +func legacyAesCipher(cipherKey string) (cipher.Block, error) { key := EncryptCipherKey(cipherKey) block, err := aes.NewCipher(key) if err != nil { diff --git a/utils/crypto_header.go b/utils/crypto_header.go new file mode 100644 index 00000000..27d5c92c --- /dev/null +++ b/utils/crypto_header.go @@ -0,0 +1,96 @@ +package utils + +import ( + "bytes" + "errors" + "strconv" +) + +type CryptoHeaderVersion int + +const ( + Headless CryptoHeaderVersion = iota + CryptoHeaderV1 +) + +const version_position = 4 +const version_v1 = 1 +const cryptor_id_position = 5 +const cryptor_id_length = 4 +const size_position = 9 +const short_size_length = 1 +const long_size_length = 3 +const long_size_indicator = 0xFF +const max_short_size = 254 + +var sentinel = []byte{0x50, 0x4E, 0x45, 0x44} + +func returnWithV1Header(cryptorId []byte, encryptedData EncryptedData) ([]byte, error) { + cryptorDataSize := len(encryptedData.CryptorData) + var cryptorDataBytesSize int + + if cryptorDataSize <= max_short_size { + cryptorDataBytesSize = short_size_length + } else { + cryptorDataBytesSize = long_size_length + } + r := make([]byte, 0, len(sentinel)+1+cryptor_id_length+cryptorDataBytesSize+cryptorDataSize+len(encryptedData.Data)) + + buffer := bytes.NewBuffer(r) + + buffer.Write(sentinel) + buffer.WriteByte(version_v1) + buffer.Write(cryptorId) + if cryptorDataBytesSize == short_size_length { + buffer.WriteByte(byte(cryptorDataSize)) + } else { + buffer.WriteByte(long_size_indicator) + buffer.Write([]byte(strconv.FormatInt(int64(cryptorDataSize), 10))) + } + buffer.Write(encryptedData.CryptorData) + buffer.Write(encryptedData.Data) + + return buffer.Bytes(), nil +} + +func slicesEqual(a, b []byte) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true +} + +func parseHeader(data []byte) ([]byte, *EncryptedData, error) { + if !slicesEqual(data[:len(sentinel)], sentinel) { + return nil, &EncryptedData{CryptorData: nil, Data: data}, nil + } + + if data[version_position] != version_v1 { + return nil, nil, errors.New("unsupported crypto header version") + } + + cryptorId := data[cryptor_id_position : cryptor_id_position+cryptor_id_length] + var headerSize int64 + var err error + var offset int64 + if data[size_position] == long_size_indicator { + offset = size_position + long_size_length + headerSize, err = strconv.ParseInt(string(data[size_position:size_position+long_size_length]), 10, 32) + if err != nil { + return nil, nil, err + } + } else { + offset = size_position + short_size_length + headerSize = int64(data[size_position]) + } + + metadata := data[offset : offset+headerSize] + offset += int64(len(metadata)) + + return cryptorId, &EncryptedData{Data: data[offset:], CryptorData: metadata}, nil +} diff --git a/utils/new_crypto.go b/utils/new_crypto.go new file mode 100644 index 00000000..43be4c69 --- /dev/null +++ b/utils/new_crypto.go @@ -0,0 +1,160 @@ +package utils + +import ( + "crypto/aes" + "crypto/cipher" + "errors" + "fmt" +) + +type Crypto struct { + encryptor Cryptor + decryptors []Cryptor + defaultDecryptor Cryptor + cryptoHeaderVersion CryptoHeaderVersion +} + +func NewCrypto(encryptor Cryptor, decryptors []Cryptor, defaultDecryptor Cryptor, version CryptoHeaderVersion) *Crypto { + return &Crypto{ + encryptor: encryptor, + decryptors: decryptors, + defaultDecryptor: defaultDecryptor, + cryptoHeaderVersion: version, + } +} + +func (c *Crypto) Encrypt(message []byte) ([]byte, error) { + if c.cryptoHeaderVersion == Headless { + r, e := c.encryptor.Encrypt(message) + if e != nil { + return nil, e + } + return r.Data, nil + } + if c.cryptoHeaderVersion == CryptoHeaderV1 { + r, e := c.encryptor.Encrypt(message) + if e != nil { + return nil, e + } + return returnWithV1Header(c.encryptor.Id(), r) + } + return nil, errors.New("unsupported crypto header version") +} + +func (c *Crypto) Decrypt(data []byte) ([]byte, error) { + cryptorId, encryptedData, err := parseHeader(data) + if err != nil { + return nil, err + } + + if cryptorId == nil { + return c.defaultDecryptor.Decrypt(*encryptedData) + } + + return c.decryptors[0].Decrypt(*encryptedData) + +} + +type Cryptor interface { + Id() []byte + Encrypt(message []byte) (EncryptedData, error) + Decrypt(encryptedData EncryptedData) ([]byte, error) +} + +type LegacyCryptor struct { + cipherKey cipher.Block + useRandomIV bool +} + +func NewLegacyCryptor(cipherKey string, useRandomIV bool) (*LegacyCryptor, error) { + block, e := legacyAesCipher(cipherKey) + if e != nil { + return nil, e + } + + return &LegacyCryptor{ + cipherKey: block, + useRandomIV: useRandomIV, + }, nil +} + +func (c *LegacyCryptor) Id() []byte { + return []byte("lega") +} + +func (c *LegacyCryptor) Encrypt(message []byte) (EncryptedData, error) { + d := encrypt(c.cipherKey, message, c.useRandomIV) + + return EncryptedData{ + CryptorData: nil, + Data: d, + }, nil +} + +func (c *LegacyCryptor) Decrypt(encryptedData EncryptedData) ([]byte, error) { + return decrypt(c.cipherKey, encryptedData.Data, c.useRandomIV) +} + +type EncryptedData struct { + CryptorData []byte + Data []byte +} + +type CBCCryptor struct { + cipherKey cipher.Block +} + +func aesCipher(cipherKey string) (cipher.Block, error) { + block, err := aes.NewCipher([]byte(cipherKey)) + if err != nil { + return nil, err + } + return block, nil +} + +func NewCBCCryptor(cipherKey string) (*CBCCryptor, error) { + block, e := aesCipher(cipherKey) + if e != nil { + return nil, e + } + + return &CBCCryptor{ + cipherKey: block, + }, nil +} + +func (c *CBCCryptor) Encrypt(message []byte) (EncryptedData, error) { + message = padWithPKCS7(message) + iv := generateIV(aes.BlockSize) + blockmode := cipher.NewCBCEncrypter(c.cipherKey, iv) + + encryptedBytes := make([]byte, len(message)) + blockmode.CryptBlocks(encryptedBytes, message) + + return EncryptedData{ + CryptorData: iv, + Data: encryptedBytes, + }, nil +} + +func (c *CBCCryptor) Decrypt(encryptedData EncryptedData) (r []byte, e error) { + decrypter := cipher.NewCBCDecrypter(c.cipherKey, encryptedData.CryptorData) + //to handle decryption errors + defer func() { + if rec := recover(); rec != nil { + r, e = nil, fmt.Errorf("decrypt error: %s", rec) + } + }() + decrypted := make([]byte, len(encryptedData.Data)) + decrypter.CryptBlocks(decrypted, encryptedData.Data) + val, err := unpadPKCS7(decrypted) + if err != nil { + return nil, fmt.Errorf("decrypt error: %s", err) + } + + return val, nil +} + +func (c *CBCCryptor) Id() []byte { + return []byte("CRIV") +} diff --git a/utils/new_crypto_test.go b/utils/new_crypto_test.go new file mode 100644 index 00000000..7b38b1fe --- /dev/null +++ b/utils/new_crypto_test.go @@ -0,0 +1,28 @@ +package utils + +import ( + "encoding/base64" + "testing" +) + +func TestJust(t *testing.T) { + legacyCryptor, e1 := NewLegacyCryptor("enigmaenigmaenig", true) + cbcCryptor, e2 := NewCBCCryptor("enigmaenigmaenig") + + if e1 != nil || e2 != nil { + t.Errorf("error, %s, %s", e1, e2) + } + + legacy := NewCrypto(legacyCryptor, []Cryptor{cbcCryptor}, legacyCryptor, Headless) + newC := NewCrypto(cbcCryptor, []Cryptor{cbcCryptor}, legacyCryptor, CryptoHeaderV1) + + r1, _ := legacy.Encrypt([]byte("aaaa")) + r2, _ := newC.Encrypt([]byte("aaaa")) + r3, _ := newC.Decrypt(r1) + r4, _ := newC.Decrypt(r2) + println(base64.StdEncoding.EncodeToString(r1)) + println(EncryptString("enigma", "aaaa", true)) + println(base64.StdEncoding.EncodeToString(r2)) + println(string(r3)) + println(string(r4)) +} From 3ce91e7cd048780ca3890a15cf76beea449bbac5 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Thu, 7 Sep 2023 13:08:46 +0200 Subject: [PATCH 02/30] Stuck on the how and when to put Header into Writer in EncryptStream --- utils/aes_cbc_crypto_algorithm.go | 94 +++++++ utils/aes_cbc_crypto_algorithm_test.go | 119 +++++++++ utils/crypto.go | 336 +++---------------------- utils/crypto_algorithm.go | 45 ++++ utils/crypto_cbc.go | 134 ++++++++++ utils/crypto_cbc_test.go | 12 + utils/crypto_header.go | 96 ------- utils/cryptor_header.go | 147 +++++++++++ utils/legacy_crypto_algorithm.go | 123 +++++++++ utils/new_crypto.go | 135 ++-------- utils/new_crypto_test.go | 10 +- utils/utils.go | 13 + 12 files changed, 746 insertions(+), 518 deletions(-) create mode 100644 utils/aes_cbc_crypto_algorithm.go create mode 100644 utils/aes_cbc_crypto_algorithm_test.go create mode 100644 utils/crypto_algorithm.go create mode 100644 utils/crypto_cbc.go create mode 100644 utils/crypto_cbc_test.go delete mode 100644 utils/crypto_header.go create mode 100644 utils/cryptor_header.go create mode 100644 utils/legacy_crypto_algorithm.go create mode 100644 utils/utils.go diff --git a/utils/aes_cbc_crypto_algorithm.go b/utils/aes_cbc_crypto_algorithm.go new file mode 100644 index 00000000..b147f918 --- /dev/null +++ b/utils/aes_cbc_crypto_algorithm.go @@ -0,0 +1,94 @@ +package utils + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/sha256" + "fmt" + "io" +) + +type CBCCryptor struct { + block cipher.Block +} + +func aesCipher(cipherKey string) (cipher.Block, error) { + hash := sha256.New() + hash.Write([]byte(cipherKey)) + + block, err := aes.NewCipher(hash.Sum(nil)) + if err != nil { + return nil, err + } + return block, nil +} + +func NewCBCCryptor(cipherKey string) (*CBCCryptor, error) { + block, e := aesCipher(cipherKey) + if e != nil { + return nil, e + } + + return &CBCCryptor{ + block: block, + }, nil +} + +func (c *CBCCryptor) Encrypt(message []byte) (EncryptedData, error) { + message = padWithPKCS7(message) + iv := generateIV(aes.BlockSize) + blockmode := cipher.NewCBCEncrypter(c.block, iv) + + encryptedBytes := make([]byte, len(message)) + blockmode.CryptBlocks(encryptedBytes, message) + + return EncryptedData{ + Metadata: iv, + Data: encryptedBytes, + }, nil +} + +func (c *CBCCryptor) Decrypt(encryptedData EncryptedData) (r []byte, e error) { + decrypter := cipher.NewCBCDecrypter(c.block, encryptedData.Metadata) + //to handle decryption errors + defer func() { + if rec := recover(); rec != nil { + r, e = nil, fmt.Errorf("decrypt error: %s", rec) + } + }() + + decrypted := make([]byte, len(encryptedData.Data)) + decrypter.CryptBlocks(decrypted, encryptedData.Data) + val, err := unpadPKCS7(decrypted) + if err != nil { + return nil, fmt.Errorf("decrypt error: %s", err) + } + + return val, nil +} + +var crivId = []byte{'C', 'R', 'I', 'V'} //("CRIV") + +func (c *CBCCryptor) Id() []byte { + return crivId +} + +func (c *CBCCryptor) HeaderVersion() CryptoHeaderVersion { + return CryptoHeaderV1 +} + +func (c *CBCCryptor) EncryptStream(input io.Reader, output io.Writer) ([]byte, error) { + iv := generateIV(aes.BlockSize) + encrypter := cipher.NewCBCEncrypter(c.block, iv) + + err := encryptStream(encrypter, input, output) + if err != nil { + return nil, err + } + return iv, nil +} + +func (c *CBCCryptor) DecryptStream(input io.Reader, metadata []byte, output io.Writer) error { + decrypter := cipher.NewCBCDecrypter(c.block, metadata) + return decryptStream(decrypter, input, output) +} diff --git a/utils/aes_cbc_crypto_algorithm_test.go b/utils/aes_cbc_crypto_algorithm_test.go new file mode 100644 index 00000000..95ed13bd --- /dev/null +++ b/utils/aes_cbc_crypto_algorithm_test.go @@ -0,0 +1,119 @@ +package utils + +import ( + "bytes" + "github.com/stretchr/testify/assert" + "strings" + "testing" + "testing/quick" +) + +func doesEncryptItWork(in []byte) bool { + cryptor, e := NewCBCCryptor("enigma") + if e != nil { + return false + } + + outputBuff := bytes.NewBuffer(make([]byte, 0, len(in))) + metadata, err := cryptor.EncryptStream(bytes.NewReader(in), outputBuff) + if err != nil { + println(err.Error()) + return false + } + + decrypted, err := cryptor.Decrypt(EncryptedData{ + Data: outputBuff.Bytes(), + Metadata: metadata, + }) + if err != nil { + println(err.Error()) + return false + } + return bytes.Equal(in, decrypted) +} + +func doesDecryptItWork(in []byte) bool { + cryptor, e := NewCBCCryptor("enigma") + if e != nil { + return false + } + + outputBuff := bytes.NewBuffer(make([]byte, 0, len(in))) + metadata, err := cryptor.EncryptStream(bytes.NewReader(in), outputBuff) + if err != nil { + println(err.Error()) + return false + } + + decrypted, err := cryptor.Decrypt(EncryptedData{ + Data: outputBuff.Bytes(), + Metadata: metadata, + }) + if err != nil { + println(err.Error()) + return false + } + return bytes.Equal(in, decrypted) +} + +func TestEncryptStreamWorks(t *testing.T) { + if err := quick.Check(doesEncryptItWork, nil); err != nil { + t.Error(err) + } +} + +func TestDecryptStreamWorks(t *testing.T) { + if err := quick.Check(doesDecryptItWork, nil); err != nil { + t.Error(err) + } +} + +func TestTest(t *testing.T) { + assert.True(t, doesEncryptItWork([]byte{0x96, 0x8f, 0xc6, 0x16, 0x7, 0x60, 0x8, 0xd2, 0xcc, 0x3e, 0xe1, 0x1a, 0x40, 0xbe, 0x75, 0xa9, 0x91, 0x82, 0x40, 0x5d, 0x92, 0xa2, 0x68, 0xd4, 0x68, 0x7, 0xfd, 0x43, 0x7, 0x9b, 0x2d, 0x77, 0x2e, 0xaa, 0xc7, 0x18, 0xe1, 0xb7})) +} + +func TestCBCCryptor_EncryptStream(t *testing.T) { + cryptor, e := NewCBCCryptor("enigma") + if e != nil { + t.Errorf("error, %s", e) + } + + message := "sure i can" + buff := bytes.NewBuffer(make([]byte, 0, len(message))) + metadata, err := cryptor.EncryptStream(strings.NewReader(message), buff) + if err != nil { + t.Errorf("error, %s", err) + } + + decrypted, err := cryptor.Decrypt(EncryptedData{Data: buff.Bytes(), Metadata: metadata}) + if err != nil { + t.Errorf("error, %s", err) + } + + a := assert.New(t) + a.Equal(message, string(decrypted)) +} + +func TestCBCCryptor_DecryptStream(t *testing.T) { + cryptor, e := NewCBCCryptor("enigma") + if e != nil { + t.Errorf("error, %s", e) + } + + message := "sure i can" + buff := bytes.NewBuffer(make([]byte, 0, len(message))) + metadata, err := cryptor.EncryptStream(strings.NewReader(message), buff) + if err != nil { + t.Errorf("error, %s", err) + } + + decryptBuff := bytes.NewBuffer(make([]byte, 0, len(message))) + + err2 := cryptor.DecryptStream(bytes.NewReader(buff.Bytes()), metadata, decryptBuff) + if err2 != nil { + t.Errorf("error, %s", err2) + } + + a := assert.New(t) + a.Equal(message, decryptBuff.String()) +} diff --git a/utils/crypto.go b/utils/crypto.go index c28e8cc4..eb759f36 100644 --- a/utils/crypto.go +++ b/utils/crypto.go @@ -2,14 +2,9 @@ package utils import ( "bytes" - "crypto/aes" - "crypto/cipher" "crypto/hmac" - "crypto/rand" "crypto/sha256" "encoding/base64" - "encoding/hex" - "errors" "fmt" "io" "os" @@ -20,6 +15,7 @@ import ( // 16 byte IV var valIV = "0123456789012345" +// EncryptString DEPRECATED // EncryptString creates the base64 encoded encrypted string using the // cipherKey. // It accepts the following parameters: @@ -29,39 +25,18 @@ var valIV = "0123456789012345" // // returns the base64 encoded encrypted string. func EncryptString(cipherKey string, message string, useRandomInitializationVector bool) string { - block, _ := legacyAesCipher(cipherKey) - - message = encodeNonASCIIChars(message) - - return base64.StdEncoding.EncodeToString(encrypt(block, []byte(message), useRandomInitializationVector)) -} - -func encrypt(block cipher.Block, value []byte, useRandomInitializationVector bool) []byte { - value = padWithPKCS7(value) - iv := make([]byte, aes.BlockSize) - if useRandomInitializationVector { - iv = generateIV(aes.BlockSize) - } else { - iv = []byte(valIV) + cryptor, e := NewLegacyCryptor(cipherKey, useRandomInitializationVector) + if e != nil { + panic(e) } - blockmode := cipher.NewCBCEncrypter(block, iv) - - cipherBytes := make([]byte, len(value)) - blockmode.CryptBlocks(cipherBytes, value) - if useRandomInitializationVector { - return append(iv, cipherBytes...) + encryptedData, e := cryptor.Encrypt([]byte(encodeNonASCIIChars(message))) + if e != nil { + panic(e) } - return cipherBytes -} - -type A struct { - I string - Interface *B -} -type B struct { - Value string + return base64.StdEncoding.EncodeToString(encryptedData.Data) } +// DecryptString DEPRECATED // DecryptString decodes encrypted string using the cipherKey // // It accepts the following parameters: @@ -71,85 +46,17 @@ type B struct { // // returns the unencoded encrypted string, // error if any. -func DecryptString(cipherKey string, message string, useRandomInitializationVector bool) ( - retVal interface{}, err error) { - if message == "" { - return "**decrypt error***", errors.New("message is empty") - } - - block, aesErr := legacyAesCipher(cipherKey) - if aesErr != nil { - return "***decrypt error***", fmt.Errorf("decrypt error aes cipher: %s", aesErr) - } - +func DecryptString(cipherKey string, message string, useRandomInitializationVector bool) (retVal interface{}, err error) { value, decodeErr := base64.StdEncoding.DecodeString(message) if decodeErr != nil { return "***decrypt error***", fmt.Errorf("decrypt error on decode: %s", decodeErr) } - val, e := decrypt(block, value, useRandomInitializationVector) - + cryptor, e := NewLegacyCryptor(cipherKey, useRandomInitializationVector) if e != nil { - return "***decrypt error***", fmt.Errorf("decrypt error: %s", e) - } - - return fmt.Sprintf("%s", string(val)), nil -} - -func decrypt(block cipher.Block, value []byte, useRandomInitializationVector bool) (retVal []byte, err error) { - iv := make([]byte, aes.BlockSize) - if useRandomInitializationVector { - iv = value[:16] - value = value[16:] - } else { - iv = []byte(valIV) - } - - decrypter := cipher.NewCBCDecrypter(block, iv) - //to handle decryption errors - defer func() { - if r := recover(); r != nil { - retVal, err = nil, fmt.Errorf("decrypt error: %s", r) - } - }() - decrypted := make([]byte, len(value)) - decrypter.CryptBlocks(decrypted, value) - val, err := unpadPKCS7(decrypted) - if err != nil { - return nil, fmt.Errorf("decrypt error: %s", err) + return nil, e } - - return val, nil -} - -// legacyAesCipher returns the cipher block -// -// It accepts the following parameters: -// cipherKey: cipher key. -// -// returns the cipher block, -// error if any. -func legacyAesCipher(cipherKey string) (cipher.Block, error) { - key := EncryptCipherKey(cipherKey) - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - return block, nil -} - -// EncryptCipherKey creates the 256 bit hex of the cipher key -// -// It accepts the following parameters: -// cipherKey: cipher key to use to decrypt. -// -// returns the 256 bit hex of the cipher key. -func EncryptCipherKey(cipherKey string) []byte { - hash := sha256.New() - hash.Write([]byte(cipherKey)) - - sha256String := hash.Sum(nil)[:16] - return []byte(hex.EncodeToString(sha256String)) + return cryptor.Decrypt(EncryptedData{Data: value, Metadata: nil}) } // encodeNonAsciiChars creates unicode string of the non-ascii chars. @@ -193,214 +100,27 @@ func GetHmacSha256(secretKey string, input string) string { return signature } -// padWithPKCS7 pads the data as per the PKCS7 standard -// It accepts the following parameters: -// data: data to pad as byte array. -// returns the padded data as byte array. -func padWithPKCS7(data []byte) []byte { - blocklen := 16 - padlen := 1 - for ((len(data) + padlen) % blocklen) != 0 { - padlen = padlen + 1 - } - - pad := bytes.Repeat([]byte{byte(padlen)}, padlen) - return append(data, pad...) -} - -// unpadPKCS7 unpads the data as per the PKCS7 standard -// It accepts the following parameters: -// data: data to unpad as byte array. -// returns the unpadded data as byte array. -func unpadPKCS7(data []byte) ([]byte, error) { - blocklen := 16 - if len(data)%blocklen != 0 || len(data) == 0 { - return nil, fmt.Errorf("invalid data len %d", len(data)) - } - padlen := int(data[len(data)-1]) - if padlen > blocklen || padlen == 0 { - return nil, fmt.Errorf("padding is invalid") - } - // check padding - pad := data[len(data)-padlen:] - for i := 0; i < padlen; i++ { - if pad[i] != byte(padlen) { - return nil, fmt.Errorf("padding is invalid") - } +// EncryptFile DEPRECATED +func EncryptFile(cipherKey string, _ []byte, filePart io.Writer, file *os.File) { + cryptor, e := NewLegacyCryptor(cipherKey, true) + if e != nil { + panic(e) } - - return data[:len(data)-padlen], nil -} - -func generateIV(blocksize int) []byte { - iv := make([]byte, aes.BlockSize) - if _, err := rand.Read(iv); err != nil { - panic(err) + _, e = cryptor.EncryptStream(file, filePart) + if e != nil { + panic(e) } - return iv } -func EncryptFile(cipherKey string, iv []byte, filePart io.Writer, file *os.File) { - key := EncryptCipherKey(cipherKey) - block, err := aes.NewCipher(key) - if err != nil { - panic(err) - } - if bytes.Equal(iv, []byte{}) { - iv = generateIV(aes.BlockSize) - } - _, e := filePart.Write(iv) +// DecryptFile DEPRECATED +func DecryptFile(cipherKey string, _ int64, reader io.Reader, w io.WriteCloser) { + cryptor, e := NewLegacyCryptor(cipherKey, true) if e != nil { panic(e) } - blockSize := 16 - bufferSize := 16 - p := make([]byte, bufferSize) - - mode := cipher.NewCBCEncrypter(block, iv) - cryptoRan := false - fii, _ := file.Stat() - contentLenIn := fii.Size() - var contentRead int64 - - for { - n2, err2 := io.ReadFull(file, p) - contentRead += int64(n2) - if err2 != nil { - if err2 == io.EOF { - ciphertext := make([]byte, blockSize) - copy(ciphertext[:n2], p[:n2]) - break - } - - if err2 == io.ErrUnexpectedEOF { - if !cryptoRan { - text := make([]byte, blockSize) - ciphertext := make([]byte, blockSize) - copy(text[:n2], p[:n2]) - pad := bytes.Repeat([]byte{byte(blockSize - n2)}, blockSize-n2) - copy(text[n2:], pad) - mode.CryptBlocks(ciphertext, text) - filePart.Write(ciphertext) - } else { - text := make([]byte, blockSize) - ciphertext := make([]byte, blockSize) - copy(text[:n2], p[:n2]) - pad := bytes.Repeat([]byte{byte(blockSize - n2)}, blockSize-n2) - copy(text[n2:], pad) - mode.CryptBlocks(ciphertext, text) - filePart.Write(ciphertext) - - } - - } - break - } - - ciphertext := make([]byte, blockSize) - cryptoRan = true - if contentRead >= contentLenIn { - pad := bytes.Repeat([]byte{byte(blockSize - n2)}, blockSize-n2) - copy(p[n2:], pad) - } - - mode.CryptBlocks(ciphertext, p) - filePart.Write(ciphertext) - - } -} - -func DecryptFile(cipherKey string, contentLenEnc int64, reader io.Reader, w io.WriteCloser) { - key := EncryptCipherKey(cipherKey) - block, err := aes.NewCipher(key) - if err != nil { - panic(err) + e = cryptor.DecryptStream(reader, nil, w) + if e != nil { + panic(e) } - blockSize := 16 - bufferSize := 16 - p := make([]byte, bufferSize) - ivBuff := make([]byte, blockSize) - emptyByteVar := make([]byte, blockSize) - - iv2 := make([]byte, blockSize) - count := 0 - - var mode cipher.BlockMode - - cryptoRan := false - var contentDownloaded int64 - - go func() { - ExitReadLabel: - for { - n2, err2 := io.ReadFull(reader, p) - if err2 != nil { - if err2 == io.EOF { - ciphertext := make([]byte, blockSize) - copy(ciphertext, p[:n2]) - ciphertext, _ = unpadPKCS7(ciphertext) - w.Write(ciphertext) - w.Close() - break ExitReadLabel - } - - if err2 == io.ErrUnexpectedEOF { - if bytes.Equal(iv2, emptyByteVar) { - copy(iv2, ivBuff[0:blockSize]) - mode = cipher.NewCBCDecrypter(block, iv2) - } - if !cryptoRan { - text := make([]byte, blockSize) - ciphertext := make([]byte, blockSize) - copy(text, p[:n2]) - mode.CryptBlocks(ciphertext, text) - ciphertext, _ = unpadPKCS7(ciphertext) - w.Write(ciphertext) - - w.Close() - break ExitReadLabel - } else { - ciphertext := make([]byte, blockSize) - copy(ciphertext, p[:n2]) - ciphertext, _ = unpadPKCS7(ciphertext) - w.Write(ciphertext) - w.Close() - break ExitReadLabel - } - - } - break ExitReadLabel - } else { - contentDownloaded += int64(n2) - if count < blockSize/bufferSize { - if err != nil { - panic(err) - } - copy(ivBuff[bufferSize*count:], p) - } else { - - if bytes.Equal(iv2, emptyByteVar) { - copy(iv2, ivBuff[0:blockSize]) - mode = cipher.NewCBCDecrypter(block, iv2) - } - - ciphertext := make([]byte, blockSize) - - text := make([]byte, blockSize) - copy(text, p[:n2]) - - mode.CryptBlocks(ciphertext, p) - cryptoRan = true - if contentDownloaded >= contentLenEnc { - ciphertext, _ = unpadPKCS7(ciphertext) - w.Write(ciphertext) - } else { - w.Write(ciphertext) - } - } - } - count++ - - } - }() + e = w.Close() } diff --git a/utils/crypto_algorithm.go b/utils/crypto_algorithm.go new file mode 100644 index 00000000..e1493dca --- /dev/null +++ b/utils/crypto_algorithm.go @@ -0,0 +1,45 @@ +package utils + +import ( + "bytes" + "io" +) + +type CryptoAlgorithm interface { + HeaderVersion() CryptoHeaderVersion + Id() []byte + Encrypt(message []byte) (EncryptedData, error) + Decrypt(encryptedData EncryptedData) ([]byte, error) + + EncryptStream(input io.Reader, output io.Writer) ([]byte, error) + DecryptStream(input io.Reader, metadata []byte, output io.Writer) error +} + +type EncryptedData struct { + Metadata []byte + Data []byte +} + +type EncryptingWriter interface { + io.Writer + WriteMetadata(in []byte) (n int, err error) +} + +type EncryptingWriterV1 struct { + w *io.Writer + metadataBuffer *bytes.Buffer + metadataDone bool +} + +func (e *EncryptingWriterV1) Write(in []byte) (n int, err error) { + e.metadataDone = true + return 0, nil +} + +func (e *EncryptingWriterV1) WriteMetadata(in []byte) error { + _, er := e.metadataBuffer.Write(in) + if er != nil { + return er + } + return nil +} diff --git a/utils/crypto_cbc.go b/utils/crypto_cbc.go new file mode 100644 index 00000000..07754605 --- /dev/null +++ b/utils/crypto_cbc.go @@ -0,0 +1,134 @@ +package utils + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "errors" + "fmt" + "io" +) + +func encrypt(mode cipher.BlockMode, input []byte) []byte { + cipherBytes := make([]byte, len(input)) + mode.CryptBlocks(cipherBytes, input) + return cipherBytes +} + +func decrypt(mode cipher.BlockMode, input []byte) (output []byte, err error) { + //to handle decryption errors + defer func() { + if r := recover(); r != nil { + output, err = nil, fmt.Errorf("decrypt error: %s", r) + } + }() + decrypted := make([]byte, len(input)) + mode.CryptBlocks(decrypted, input) + val, err := unpadPKCS7(decrypted) + if err != nil { + return nil, fmt.Errorf("decrypt error: %s", err) + } + + return val, nil +} + +func encryptStream(mode cipher.BlockMode, input io.Reader, output io.Writer) error { + readDataSlice := make([]byte, aes.BlockSize) + for { + sizeOfCurrentlyRead, readErr := io.ReadFull(input, readDataSlice) + + if readErr != nil && readErr != io.EOF && !errors.Is(readErr, io.ErrUnexpectedEOF) { + return readErr + } + + if sizeOfCurrentlyRead < aes.BlockSize { + pad := bytes.Repeat([]byte{byte(aes.BlockSize - sizeOfCurrentlyRead)}, aes.BlockSize-sizeOfCurrentlyRead) + copy(readDataSlice[sizeOfCurrentlyRead:], pad) + } + + mode.CryptBlocks(readDataSlice, readDataSlice) + _, writeErr := output.Write(readDataSlice) + if writeErr != nil { + return writeErr + } + + if readErr == io.EOF || errors.Is(readErr, io.ErrUnexpectedEOF) { + break + } + + } + + return nil +} + +func decryptStream(mode cipher.BlockMode, input io.Reader, output io.Writer) error { + readDataSlice := make([]byte, aes.BlockSize) + + for { + _, readErr := io.ReadFull(input, readDataSlice) + + if readErr != nil && readErr != io.EOF { + return readErr + } + + if readErr == io.EOF { + break + } + + mode.CryptBlocks(readDataSlice, readDataSlice) + unpadded, unpadError := unpadPKCS7(readDataSlice) + if unpadError != nil { + return unpadError + } + _, writeErr := output.Write(unpadded) + if writeErr != nil { + return writeErr + } + + if errors.Is(readErr, io.ErrUnexpectedEOF) { + break + } + + } + + return nil +} + +// padWithPKCS7 pads the data as per the PKCS7 standard +// It accepts the following parameters: +// data: data to pad as byte array. +// returns the padded data as byte array. +func padWithPKCS7(data []byte) []byte { + blocklen := 16 + padlen := 1 + for ((len(data) + padlen) % blocklen) != 0 { + padlen = padlen + 1 + } + + pad := bytes.Repeat([]byte{byte(padlen)}, padlen) + return append(data, pad...) +} + +// unpadPKCS7 unpads the data as per the PKCS7 standard +// It accepts the following parameters: +// data: data to unpad as byte array. +// returns the unpadded data as byte array. +func unpadPKCS7(data []byte) ([]byte, error) { + blocklen := 16 + if len(data)%blocklen != 0 || len(data) == 0 { + return nil, fmt.Errorf("invalid data len %d", len(data)) + } + padlen := int(data[len(data)-1]) + if padlen > blocklen || padlen == 0 { + return nil, fmt.Errorf("padding is invalid") + } + // check padding + pad := data[len(data)-padlen:] + for i := 0; i < padlen; i++ { + if pad[i] != byte(padlen) { + return nil, fmt.Errorf("padding is invalid") + } + } + + return data[:len(data)-padlen], nil +} diff --git a/utils/crypto_cbc_test.go b/utils/crypto_cbc_test.go new file mode 100644 index 00000000..ca6d342b --- /dev/null +++ b/utils/crypto_cbc_test.go @@ -0,0 +1,12 @@ +package utils + +import "testing" + +func TestUnnpad(t *testing.T) { + in := []byte{0xbe, 0x77, 0x44, 0xee, 0xcd, 0xc8, 0x5f, 0xf, 0xec, 0x20, 0x6c, 0x2d, 0x86, 0x7c, 0x23, 0xeb} + pkcs7, err := unpadPKCS7(in) + if err != nil { + t.Errorf("error, %s", err) + } + println(string(pkcs7)) +} diff --git a/utils/crypto_header.go b/utils/crypto_header.go deleted file mode 100644 index 27d5c92c..00000000 --- a/utils/crypto_header.go +++ /dev/null @@ -1,96 +0,0 @@ -package utils - -import ( - "bytes" - "errors" - "strconv" -) - -type CryptoHeaderVersion int - -const ( - Headless CryptoHeaderVersion = iota - CryptoHeaderV1 -) - -const version_position = 4 -const version_v1 = 1 -const cryptor_id_position = 5 -const cryptor_id_length = 4 -const size_position = 9 -const short_size_length = 1 -const long_size_length = 3 -const long_size_indicator = 0xFF -const max_short_size = 254 - -var sentinel = []byte{0x50, 0x4E, 0x45, 0x44} - -func returnWithV1Header(cryptorId []byte, encryptedData EncryptedData) ([]byte, error) { - cryptorDataSize := len(encryptedData.CryptorData) - var cryptorDataBytesSize int - - if cryptorDataSize <= max_short_size { - cryptorDataBytesSize = short_size_length - } else { - cryptorDataBytesSize = long_size_length - } - r := make([]byte, 0, len(sentinel)+1+cryptor_id_length+cryptorDataBytesSize+cryptorDataSize+len(encryptedData.Data)) - - buffer := bytes.NewBuffer(r) - - buffer.Write(sentinel) - buffer.WriteByte(version_v1) - buffer.Write(cryptorId) - if cryptorDataBytesSize == short_size_length { - buffer.WriteByte(byte(cryptorDataSize)) - } else { - buffer.WriteByte(long_size_indicator) - buffer.Write([]byte(strconv.FormatInt(int64(cryptorDataSize), 10))) - } - buffer.Write(encryptedData.CryptorData) - buffer.Write(encryptedData.Data) - - return buffer.Bytes(), nil -} - -func slicesEqual(a, b []byte) bool { - if len(a) != len(b) { - return false - } - for i, v := range a { - if v != b[i] { - return false - } - } - return true -} - -func parseHeader(data []byte) ([]byte, *EncryptedData, error) { - if !slicesEqual(data[:len(sentinel)], sentinel) { - return nil, &EncryptedData{CryptorData: nil, Data: data}, nil - } - - if data[version_position] != version_v1 { - return nil, nil, errors.New("unsupported crypto header version") - } - - cryptorId := data[cryptor_id_position : cryptor_id_position+cryptor_id_length] - var headerSize int64 - var err error - var offset int64 - if data[size_position] == long_size_indicator { - offset = size_position + long_size_length - headerSize, err = strconv.ParseInt(string(data[size_position:size_position+long_size_length]), 10, 32) - if err != nil { - return nil, nil, err - } - } else { - offset = size_position + short_size_length - headerSize = int64(data[size_position]) - } - - metadata := data[offset : offset+headerSize] - offset += int64(len(metadata)) - - return cryptorId, &EncryptedData{Data: data[offset:], CryptorData: metadata}, nil -} diff --git a/utils/cryptor_header.go b/utils/cryptor_header.go new file mode 100644 index 00000000..a729f667 --- /dev/null +++ b/utils/cryptor_header.go @@ -0,0 +1,147 @@ +package utils + +import ( + "bufio" + "bytes" + "errors" + "io" + "strconv" +) + +type CryptoHeaderVersion int + +const ( + headless CryptoHeaderVersion = iota + CryptoHeaderV1 +) + +const versionPosition = 4 +const versionV1 = 1 +const cryptorIdPosition = 5 +const cryptorIdLength = 4 +const sizePosition = 9 +const shortSizeLength = 1 +const longSizeLength = 3 +const longSizeIndicator = 0xFF +const maxShortSize = 254 +const sentinelLength = 4 + +var sentinel = [sentinelLength]byte{0x50, 0x4E, 0x45, 0x44} + +func returnWithV1Header(cryptorId []byte, encryptedData EncryptedData) ([]byte, error) { + cryptorDataSize := len(encryptedData.Metadata) + var cryptorDataBytesSize int + + if cryptorDataSize <= maxShortSize { + cryptorDataBytesSize = shortSizeLength + } else { + cryptorDataBytesSize = longSizeLength + } + r := make([]byte, 0, len(sentinel)+1+cryptorIdLength+cryptorDataBytesSize+cryptorDataSize+len(encryptedData.Data)) + + buffer := bytes.NewBuffer(r) + buffer.Write(sentinel[:]) + buffer.WriteByte(versionV1) + buffer.Write(cryptorId) + if cryptorDataBytesSize == shortSizeLength { + buffer.WriteByte(byte(cryptorDataSize)) + } else { + buffer.WriteByte(longSizeIndicator) + buffer.Write([]byte(strconv.FormatInt(int64(cryptorDataSize), 10))) + } + buffer.Write(encryptedData.Metadata) + buffer.Write(encryptedData.Data) + + return buffer.Bytes(), nil +} + +func slicesEqual(a, b []byte) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true +} + +func ParseHeader(data []byte) ([]byte, *EncryptedData, error) { + if !slicesEqual(data[:len(sentinel)], sentinel[:]) { + return nil, &EncryptedData{Metadata: nil, Data: data}, nil + } + + if data[versionPosition] != versionV1 { + return nil, nil, errors.New("unsupported crypto header version") + } + + cryptorId := data[cryptorIdPosition : cryptorIdPosition+cryptorIdLength] + var headerSize int64 + position := int64(sizePosition) + if data[sizePosition] == longSizeIndicator { + position += longSizeLength + var err error + headerSize, err = strconv.ParseInt(string(data[sizePosition:sizePosition+longSizeLength]), 10, 32) + if err != nil { + return nil, nil, err + } + } else { + position += shortSizeLength + headerSize = int64(data[sizePosition]) + } + + metadata := data[position : position+headerSize] + position += int64(len(metadata)) + + return cryptorId, &EncryptedData{Data: data[position:], Metadata: metadata}, nil +} + +func ParseHeaderStream(data io.Reader) ([]byte, io.Reader, []byte, error) { + bufData := bufio.NewReader(data) + + peeked, err := bufData.Peek(sentinelLength + 1 + cryptorIdLength + longSizeLength) + if err != nil { + return nil, nil, nil, err + } + + if !slicesEqual(peeked[:len(sentinel)], sentinel[:]) { + return nil, bufData, nil, nil + } + + if peeked[versionPosition] != versionV1 { + return nil, nil, nil, errors.New("unsupported crypto header version") + } + + cryptorId := peeked[cryptorIdPosition : cryptorIdPosition+cryptorIdLength] + var headerSize int64 + position := int64(sizePosition) + if peeked[sizePosition] == longSizeIndicator { + position += longSizeLength + var e error + headerSize, e = strconv.ParseInt(string(peeked[sizePosition:sizePosition+longSizeLength]), 10, 32) + if e != nil { + return nil, nil, nil, e + } + } else { + position += shortSizeLength + headerSize = int64(peeked[sizePosition]) + } + if headerSize > 254 { + _, e := bufData.Discard(sentinelLength + 1 + cryptorIdLength + longSizeLength) + if e != nil { + return nil, nil, nil, e + } + } else { + _, e := bufData.Discard(sentinelLength + 1 + cryptorIdLength + shortSizeLength) + if e != nil { + return nil, nil, nil, e + } + } + metadata := make([]byte, headerSize) + _, e := io.ReadFull(bufData, metadata) + if e != nil { + return nil, nil, nil, e + } + return cryptorId, bufData, metadata, nil +} diff --git a/utils/legacy_crypto_algorithm.go b/utils/legacy_crypto_algorithm.go new file mode 100644 index 00000000..ceb648f3 --- /dev/null +++ b/utils/legacy_crypto_algorithm.go @@ -0,0 +1,123 @@ +package utils + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/sha256" + "encoding/hex" + "io" +) + +type LegacyCryptoAlgorithm struct { + block cipher.Block + useRandomIvForSlices bool +} + +func NewLegacyCryptor(cipherKey string, useRandomIV bool) (*LegacyCryptoAlgorithm, error) { + block, e := legacyAesCipher(cipherKey) + if e != nil { + return nil, e + } + + return &LegacyCryptoAlgorithm{ + block: block, + useRandomIvForSlices: useRandomIV, + }, nil +} + +var legacyId = []byte{0x00, 0x00, 0x00, 0x00} + +func (c *LegacyCryptoAlgorithm) Id() []byte { + return legacyId +} + +func (c *LegacyCryptoAlgorithm) HeaderVersion() CryptoHeaderVersion { + return headless +} + +func (c *LegacyCryptoAlgorithm) Encrypt(value []byte) (EncryptedData, error) { + value = padWithPKCS7(value) + iv := make([]byte, aes.BlockSize) + if c.useRandomIvForSlices { + iv = generateIV(aes.BlockSize) + } else { + iv = []byte(valIV) + } + + blockmode := cipher.NewCBCEncrypter(c.block, iv) + cipherBytes := encrypt(blockmode, value) + + if c.useRandomIvForSlices { + return EncryptedData{Data: append(iv, cipherBytes...), Metadata: nil}, nil + } + return EncryptedData{Data: cipherBytes, Metadata: iv}, nil +} + +func (c *LegacyCryptoAlgorithm) Decrypt(encryptedData EncryptedData) ([]byte, error) { + iv := make([]byte, aes.BlockSize) + if c.useRandomIvForSlices { + iv = generateIV(aes.BlockSize) //TODO + } else { + iv = []byte(valIV) + } + + decrypter := cipher.NewCBCDecrypter(c.block, iv) + + return decrypt(decrypter, encryptedData.Data) +} + +func (c *LegacyCryptoAlgorithm) EncryptStream(input io.Reader, output io.Writer) ([]byte, error) { + iv := generateIV(aes.BlockSize) + _, err := output.Write(iv) + if err != nil { + return nil, err + } + blockmode := cipher.NewCBCEncrypter(c.block, iv) + err = encryptStream(blockmode, input, output) + if err != nil { + return nil, err + } + return nil, nil +} + +func (c *LegacyCryptoAlgorithm) DecryptStream(input io.Reader, _ []byte, output io.Writer) error { + iv := make([]byte, aes.BlockSize) + _, err := io.ReadFull(input, iv) + if err != nil { + return nil + } + + blockmode := cipher.NewCBCEncrypter(c.block, iv) + return decryptStream(blockmode, input, output) +} + +// EncryptCipherKey DEPRECATED +// EncryptCipherKey creates the 256 bit hex of the cipher key +// +// It accepts the following parameters: +// cipherKey: cipher key to use to decrypt. +// +// returns the 256 bit hex of the cipher key. +func EncryptCipherKey(cipherKey string) []byte { + hash := sha256.New() + hash.Write([]byte(cipherKey)) + + sha256String := hash.Sum(nil)[:16] + return []byte(hex.EncodeToString(sha256String)) +} + +// legacyAesCipher returns the cipher block +// +// It accepts the following parameters: +// cipherKey: cipher key. +// +// returns the cipher block, +// error if any. +func legacyAesCipher(cipherKey string) (cipher.Block, error) { + key := EncryptCipherKey(cipherKey) + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return block, nil +} diff --git a/utils/new_crypto.go b/utils/new_crypto.go index 43be4c69..6848a19e 100644 --- a/utils/new_crypto.go +++ b/utils/new_crypto.go @@ -1,20 +1,18 @@ package utils import ( - "crypto/aes" - "crypto/cipher" "errors" - "fmt" + "io" ) type Crypto struct { - encryptor Cryptor - decryptors []Cryptor - defaultDecryptor Cryptor + encryptor CryptoAlgorithm + decryptors []CryptoAlgorithm + defaultDecryptor CryptoAlgorithm cryptoHeaderVersion CryptoHeaderVersion } -func NewCrypto(encryptor Cryptor, decryptors []Cryptor, defaultDecryptor Cryptor, version CryptoHeaderVersion) *Crypto { +func NewCrypto(encryptor CryptoAlgorithm, decryptors []CryptoAlgorithm, defaultDecryptor CryptoAlgorithm, version CryptoHeaderVersion) *Crypto { return &Crypto{ encryptor: encryptor, decryptors: decryptors, @@ -24,25 +22,21 @@ func NewCrypto(encryptor Cryptor, decryptors []Cryptor, defaultDecryptor Cryptor } func (c *Crypto) Encrypt(message []byte) ([]byte, error) { - if c.cryptoHeaderVersion == Headless { - r, e := c.encryptor.Encrypt(message) - if e != nil { - return nil, e - } + r, e := c.encryptor.Encrypt(message) + if e != nil { + return nil, e + } + if c.encryptor.HeaderVersion() == headless { return r.Data, nil } if c.cryptoHeaderVersion == CryptoHeaderV1 { - r, e := c.encryptor.Encrypt(message) - if e != nil { - return nil, e - } return returnWithV1Header(c.encryptor.Id(), r) } return nil, errors.New("unsupported crypto header version") } func (c *Crypto) Decrypt(data []byte) ([]byte, error) { - cryptorId, encryptedData, err := parseHeader(data) + cryptorId, encryptedData, err := ParseHeader(data) if err != nil { return nil, err } @@ -52,109 +46,32 @@ func (c *Crypto) Decrypt(data []byte) ([]byte, error) { } return c.decryptors[0].Decrypt(*encryptedData) - -} - -type Cryptor interface { - Id() []byte - Encrypt(message []byte) (EncryptedData, error) - Decrypt(encryptedData EncryptedData) ([]byte, error) -} - -type LegacyCryptor struct { - cipherKey cipher.Block - useRandomIV bool } -func NewLegacyCryptor(cipherKey string, useRandomIV bool) (*LegacyCryptor, error) { - block, e := legacyAesCipher(cipherKey) +func (c *Crypto) EncryptStream(input io.Reader, output io.Writer) error { + _, e := c.encryptor.EncryptStream(input, output) if e != nil { - return nil, e + return e } - - return &LegacyCryptor{ - cipherKey: block, - useRandomIV: useRandomIV, - }, nil -} - -func (c *LegacyCryptor) Id() []byte { - return []byte("lega") -} - -func (c *LegacyCryptor) Encrypt(message []byte) (EncryptedData, error) { - d := encrypt(c.cipherKey, message, c.useRandomIV) - - return EncryptedData{ - CryptorData: nil, - Data: d, - }, nil -} - -func (c *LegacyCryptor) Decrypt(encryptedData EncryptedData) ([]byte, error) { - return decrypt(c.cipherKey, encryptedData.Data, c.useRandomIV) -} - -type EncryptedData struct { - CryptorData []byte - Data []byte -} - -type CBCCryptor struct { - cipherKey cipher.Block -} - -func aesCipher(cipherKey string) (cipher.Block, error) { - block, err := aes.NewCipher([]byte(cipherKey)) - if err != nil { - return nil, err + if c.encryptor.HeaderVersion() == headless { + return nil } - return block, nil -} -func NewCBCCryptor(cipherKey string) (*CBCCryptor, error) { - block, e := aesCipher(cipherKey) - if e != nil { - return nil, e + if c.cryptoHeaderVersion == CryptoHeaderV1 { + return nil //TODO it's too late to add header now } - - return &CBCCryptor{ - cipherKey: block, - }, nil + return nil //TODO } -func (c *CBCCryptor) Encrypt(message []byte) (EncryptedData, error) { - message = padWithPKCS7(message) - iv := generateIV(aes.BlockSize) - blockmode := cipher.NewCBCEncrypter(c.cipherKey, iv) - - encryptedBytes := make([]byte, len(message)) - blockmode.CryptBlocks(encryptedBytes, message) - - return EncryptedData{ - CryptorData: iv, - Data: encryptedBytes, - }, nil -} - -func (c *CBCCryptor) Decrypt(encryptedData EncryptedData) (r []byte, e error) { - decrypter := cipher.NewCBCDecrypter(c.cipherKey, encryptedData.CryptorData) - //to handle decryption errors - defer func() { - if rec := recover(); rec != nil { - r, e = nil, fmt.Errorf("decrypt error: %s", rec) - } - }() - decrypted := make([]byte, len(encryptedData.Data)) - decrypter.CryptBlocks(decrypted, encryptedData.Data) - val, err := unpadPKCS7(decrypted) +func (c *Crypto) DecryptStream(input io.Reader, output io.Writer) error { + cryptorId, reader, metadata, err := ParseHeaderStream(input) if err != nil { - return nil, fmt.Errorf("decrypt error: %s", err) + return err } - return val, nil -} + if cryptorId == nil { + return c.defaultDecryptor.DecryptStream(reader, metadata, output) + } -func (c *CBCCryptor) Id() []byte { - return []byte("CRIV") + return c.decryptors[0].DecryptStream(reader, metadata, output) } diff --git a/utils/new_crypto_test.go b/utils/new_crypto_test.go index 7b38b1fe..de2108d6 100644 --- a/utils/new_crypto_test.go +++ b/utils/new_crypto_test.go @@ -13,15 +13,15 @@ func TestJust(t *testing.T) { t.Errorf("error, %s, %s", e1, e2) } - legacy := NewCrypto(legacyCryptor, []Cryptor{cbcCryptor}, legacyCryptor, Headless) - newC := NewCrypto(cbcCryptor, []Cryptor{cbcCryptor}, legacyCryptor, CryptoHeaderV1) + legacy := NewCrypto(legacyCryptor, []CryptoAlgorithm{cbcCryptor}, legacyCryptor, headless) + newC := NewCrypto(cbcCryptor, []CryptoAlgorithm{cbcCryptor}, legacyCryptor, CryptoHeaderV1) - r1, _ := legacy.Encrypt([]byte("aaaa")) - r2, _ := newC.Encrypt([]byte("aaaa")) + r1, _ := legacy.Encrypt([]byte("sure i can")) + r2, _ := newC.Encrypt([]byte("sure i can")) r3, _ := newC.Decrypt(r1) r4, _ := newC.Decrypt(r2) println(base64.StdEncoding.EncodeToString(r1)) - println(EncryptString("enigma", "aaaa", true)) + println(EncryptString("enigma", "sure i can", true)) println(base64.StdEncoding.EncodeToString(r2)) println(string(r3)) println(string(r4)) diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 00000000..cd304327 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,13 @@ +package utils + +import ( + "crypto/rand" +) + +func generateIV(blocksize int) []byte { + iv := make([]byte, blocksize) + if _, err := rand.Read(iv); err != nil { + panic(err) + } + return iv +} From 2958f3083f571fd9430b726a95fd369a036e7823 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Wed, 13 Sep 2023 16:55:54 +0200 Subject: [PATCH 03/30] Implementation + property based tests of reader enc/dec --- {utils => crypto}/aes_cbc_crypto_algorithm.go | 59 +++--- crypto/aes_cbc_crypto_algorithm_test.go | 75 +++++++ crypto/crypto_algorithm.go | 25 +++ crypto/crypto_module.go | 90 +++++++++ crypto/crypto_module_test.go | 28 +++ crypto/cryptor.go | 190 ++++++++++++++++++ {utils => crypto}/cryptor_header.go | 93 +++++++-- crypto/decrypting_reader.go | 134 ++++++++++++ crypto/decrypting_reader_test.go | 49 +++++ crypto/encrypting_reader.go | 86 ++++++++ crypto/encrypting_reader_test.go | 43 ++++ {utils => crypto}/legacy_crypto_algorithm.go | 76 +++---- crypto/legacy_crypto_algorithm_test.go | 75 +++++++ crypto/utils.go | 57 ++++++ utils/aes_cbc_crypto_algorithm_test.go | 119 ----------- utils/crypto.go | 42 ++-- utils/crypto_algorithm.go | 45 ----- utils/crypto_cbc.go | 134 ------------ utils/crypto_cbc_test.go | 12 -- utils/new_crypto.go | 77 ------- utils/new_crypto_test.go | 28 --- utils/utils.go | 13 -- 22 files changed, 1022 insertions(+), 528 deletions(-) rename {utils => crypto}/aes_cbc_crypto_algorithm.go (56%) create mode 100644 crypto/aes_cbc_crypto_algorithm_test.go create mode 100644 crypto/crypto_algorithm.go create mode 100644 crypto/crypto_module.go create mode 100644 crypto/crypto_module_test.go create mode 100644 crypto/cryptor.go rename {utils => crypto}/cryptor_header.go (57%) create mode 100644 crypto/decrypting_reader.go create mode 100644 crypto/decrypting_reader_test.go create mode 100644 crypto/encrypting_reader.go create mode 100644 crypto/encrypting_reader_test.go rename {utils => crypto}/legacy_crypto_algorithm.go (50%) create mode 100644 crypto/legacy_crypto_algorithm_test.go create mode 100644 crypto/utils.go delete mode 100644 utils/aes_cbc_crypto_algorithm_test.go delete mode 100644 utils/crypto_algorithm.go delete mode 100644 utils/crypto_cbc.go delete mode 100644 utils/crypto_cbc_test.go delete mode 100644 utils/new_crypto.go delete mode 100644 utils/new_crypto_test.go delete mode 100644 utils/utils.go diff --git a/utils/aes_cbc_crypto_algorithm.go b/crypto/aes_cbc_crypto_algorithm.go similarity index 56% rename from utils/aes_cbc_crypto_algorithm.go rename to crypto/aes_cbc_crypto_algorithm.go index b147f918..f3a22c17 100644 --- a/utils/aes_cbc_crypto_algorithm.go +++ b/crypto/aes_cbc_crypto_algorithm.go @@ -1,4 +1,4 @@ -package utils +package crypto import ( "crypto/aes" @@ -8,33 +8,28 @@ import ( "io" ) -type CBCCryptor struct { +type AesCBCCryptoAlgorithm struct { block cipher.Block } -func aesCipher(cipherKey string) (cipher.Block, error) { - hash := sha256.New() - hash.Write([]byte(cipherKey)) - - block, err := aes.NewCipher(hash.Sum(nil)) - if err != nil { - return nil, err - } - return block, nil -} - -func NewCBCCryptor(cipherKey string) (*CBCCryptor, error) { +func NewAesCBCCryptoAlgorithm(cipherKey string) (*AesCBCCryptoAlgorithm, error) { block, e := aesCipher(cipherKey) if e != nil { return nil, e } - return &CBCCryptor{ + return &AesCBCCryptoAlgorithm{ block: block, }, nil } -func (c *CBCCryptor) Encrypt(message []byte) (EncryptedData, error) { +var crivId = "CRIV" + +func (c *AesCBCCryptoAlgorithm) Id() string { + return crivId +} + +func (c *AesCBCCryptoAlgorithm) Encrypt(message []byte) (*EncryptedData, error) { message = padWithPKCS7(message) iv := generateIV(aes.BlockSize) blockmode := cipher.NewCBCEncrypter(c.block, iv) @@ -42,13 +37,13 @@ func (c *CBCCryptor) Encrypt(message []byte) (EncryptedData, error) { encryptedBytes := make([]byte, len(message)) blockmode.CryptBlocks(encryptedBytes, message) - return EncryptedData{ + return &EncryptedData{ Metadata: iv, Data: encryptedBytes, }, nil } -func (c *CBCCryptor) Decrypt(encryptedData EncryptedData) (r []byte, e error) { +func (c *AesCBCCryptoAlgorithm) Decrypt(encryptedData *EncryptedData) (r []byte, e error) { decrypter := cipher.NewCBCDecrypter(c.block, encryptedData.Metadata) //to handle decryption errors defer func() { @@ -67,28 +62,26 @@ func (c *CBCCryptor) Decrypt(encryptedData EncryptedData) (r []byte, e error) { return val, nil } -var crivId = []byte{'C', 'R', 'I', 'V'} //("CRIV") +func (c *AesCBCCryptoAlgorithm) EncryptStream(reader io.Reader) (*EncryptedStreamData, error) { + iv := generateIV(aes.BlockSize) -func (c *CBCCryptor) Id() []byte { - return crivId + return &EncryptedStreamData{ + Metadata: iv, + Reader: NewBlockModeEncryptingReader(reader, cipher.NewCBCEncrypter(c.block, iv)), + }, nil } -func (c *CBCCryptor) HeaderVersion() CryptoHeaderVersion { - return CryptoHeaderV1 +func (c *AesCBCCryptoAlgorithm) DecryptStream(encryptedData *EncryptedStreamData) (io.Reader, error) { + return NewBlockModeDecryptingReader(encryptedData.Reader, cipher.NewCBCDecrypter(c.block, encryptedData.Metadata)), nil } -func (c *CBCCryptor) EncryptStream(input io.Reader, output io.Writer) ([]byte, error) { - iv := generateIV(aes.BlockSize) - encrypter := cipher.NewCBCEncrypter(c.block, iv) +func aesCipher(cipherKey string) (cipher.Block, error) { + hash := sha256.New() + hash.Write([]byte(cipherKey)) - err := encryptStream(encrypter, input, output) + block, err := aes.NewCipher(hash.Sum(nil)) if err != nil { return nil, err } - return iv, nil -} - -func (c *CBCCryptor) DecryptStream(input io.Reader, metadata []byte, output io.Writer) error { - decrypter := cipher.NewCBCDecrypter(c.block, metadata) - return decryptStream(decrypter, input, output) + return block, nil } diff --git a/crypto/aes_cbc_crypto_algorithm_test.go b/crypto/aes_cbc_crypto_algorithm_test.go new file mode 100644 index 00000000..d022d8ea --- /dev/null +++ b/crypto/aes_cbc_crypto_algorithm_test.go @@ -0,0 +1,75 @@ +package crypto + +import ( + "bytes" + "io" + "testing" + "testing/quick" +) + +func canDecryptEncryptStreamResult(in []byte) bool { + cryptoAlgorithm, e := NewAesCBCCryptoAlgorithm("enigma") + if e != nil { + return false + } + + output, err := cryptoAlgorithm.EncryptStream(bytes.NewReader(in)) + if err != nil { + return false + } + + encryptedData, err := io.ReadAll(output.Reader) + + decrypted, err := cryptoAlgorithm.Decrypt(&EncryptedData{ + Data: encryptedData, + Metadata: output.Metadata, + }) + if err != nil { + return false + } + return bytes.Equal(in, decrypted) +} + +func canDecryptStreamEncryptResult(in []byte) bool { + cryptor, e := NewAesCBCCryptoAlgorithm("enigma") + if e != nil { + return false + } + + output, err := cryptor.Encrypt(in) + if err != nil { + println(err.Error()) + return false + } + + decryptingReader, err := cryptor.DecryptStream(&EncryptedStreamData{ + Reader: bytes.NewReader(output.Data), + Metadata: output.Metadata, + }) + if err != nil { + println(err.Error()) + return false + } + + decrypted, err := io.ReadAll(decryptingReader) + if err != nil { + println(err.Error()) + return false + } + + return bytes.Equal(in, decrypted) +} + +func Test_AesCBC_EncryptStream(t *testing.T) { + c := quick.Config{MaxCount: 10000} + if err := quick.Check(canDecryptEncryptStreamResult, &c); err != nil { + t.Error(err) + } +} + +func Test_AesCBC_DecryptStream(t *testing.T) { + c := quick.Config{MaxCount: 10000} + if err := quick.Check(canDecryptStreamEncryptResult, &c); err != nil { + t.Error(err) + } +} diff --git a/crypto/crypto_algorithm.go b/crypto/crypto_algorithm.go new file mode 100644 index 00000000..945cdf24 --- /dev/null +++ b/crypto/crypto_algorithm.go @@ -0,0 +1,25 @@ +package crypto + +import "io" + +type EncryptedData struct { + Metadata []byte + Data []byte +} + +type CryptoAlgorithm interface { + Id() string + Encrypt(message []byte) (*EncryptedData, error) + Decrypt(encryptedData *EncryptedData) ([]byte, error) +} + +type EncryptedStreamData struct { + Metadata []byte + Reader io.Reader +} + +type ExtendedCryptoAlgorithm interface { + CryptoAlgorithm + EncryptStream(reader io.Reader) (*EncryptedStreamData, error) + DecryptStream(encryptedData *EncryptedStreamData) (io.Reader, error) +} diff --git a/crypto/crypto_module.go b/crypto/crypto_module.go new file mode 100644 index 00000000..3e77997e --- /dev/null +++ b/crypto/crypto_module.go @@ -0,0 +1,90 @@ +package crypto + +import ( + "bufio" + "io" +) + +type Module struct { + encryptor Cryptor + decryptors map[string]Cryptor + fallbackDecryptor Cryptor +} + +func NewLegacyCryptoModule(cipherKey string, randomIv bool) (*Module, error) { + legacyCryptoAlgorithm, e := NewLegacyCryptoAlgorithm(cipherKey, randomIv) + if e != nil { + return nil, e + } + aesCBCCryptoAlgorithm, e := NewAesCBCCryptoAlgorithm(cipherKey) + + if e != nil { + return nil, e + } + + decryptors := []CryptoAlgorithm{aesCBCCryptoAlgorithm} + return newCryptoModule(legacyCryptoAlgorithm, decryptors, legacyCryptoAlgorithm), nil +} + +func NewCryptoModule(cipherKey string, randomIv bool) (*Module, error) { + aesCBCCryptoAlgorithm, e := NewAesCBCCryptoAlgorithm(cipherKey) + if e != nil { + return nil, e + } + + legacyCryptoAlgorithm, e := NewLegacyCryptoAlgorithm(cipherKey, randomIv) + if e != nil { + return nil, e + } + + decryptors := []CryptoAlgorithm{aesCBCCryptoAlgorithm} + return newCryptoModule(aesCBCCryptoAlgorithm, decryptors, legacyCryptoAlgorithm), nil +} + +func newCryptoModule(encryptingAlgorithm CryptoAlgorithm, decryptingAlgorithms []CryptoAlgorithm, fallbackDecryptingAlgorithm CryptoAlgorithm) *Module { + + decryptors := make(map[string]Cryptor, len(decryptingAlgorithms)) + for _, decryptor := range decryptingAlgorithms { + decryptors[decryptor.Id()] = NewCryptor(decryptor) + } + + encryptor := NewCryptor(encryptingAlgorithm) + fallbackDecryptor := NewCryptor(fallbackDecryptingAlgorithm) + + return &Module{ + encryptor: encryptor, + decryptors: decryptors, + fallbackDecryptor: fallbackDecryptor, + } +} + +func (c *Module) Encrypt(message []byte) ([]byte, error) { + return c.encryptor.Encrypt(message) +} + +func (c *Module) Decrypt(data []byte) ([]byte, error) { + cryptorId, e := peekHeaderCryptorId(data) + if e != nil { + return nil, e + } + if cryptorId == nil { + return c.fallbackDecryptor.Decrypt(data) + } + return c.decryptors[*cryptorId].Decrypt(data) +} + +func (c *Module) EncryptStream(input io.Reader) (io.Reader, error) { + return c.encryptor.EncryptStream(input) +} + +func (c *Module) DecryptStream(input io.Reader) (io.Reader, error) { + data := bufio.NewReader(input) + cryptorId, e := peekHeaderStreamCryptorId(data) + if e != nil { + return nil, e + } + if cryptorId == nil { + return c.fallbackDecryptor.DecryptStream(data) + } + return c.decryptors[*cryptorId].DecryptStream(input) +} diff --git a/crypto/crypto_module_test.go b/crypto/crypto_module_test.go new file mode 100644 index 00000000..65e861d3 --- /dev/null +++ b/crypto/crypto_module_test.go @@ -0,0 +1,28 @@ +package crypto + +import ( + "encoding/base64" + "testing" +) + +func TestJust(t *testing.T) { + cipherKey := "enigma" + + legacy, e := NewLegacyCryptoModule(cipherKey, true) + if e != nil { + t.Errorf(e.Error()) + } + newC, e := NewCryptoModule(cipherKey, true) + if e != nil { + t.Errorf(e.Error()) + } + + r1, _ := legacy.Encrypt([]byte("sure i can")) + r2, _ := newC.Encrypt([]byte("sure i can")) + r3, _ := newC.Decrypt(r1) + r4, _ := newC.Decrypt(r2) + println(base64.StdEncoding.EncodeToString(r1)) + println(base64.StdEncoding.EncodeToString(r2)) + println(string(r3)) + println(string(r4)) +} diff --git a/crypto/cryptor.go b/crypto/cryptor.go new file mode 100644 index 00000000..19bb6746 --- /dev/null +++ b/crypto/cryptor.go @@ -0,0 +1,190 @@ +package crypto + +import ( + "bytes" + "io" +) + +type Cryptor interface { + Id() string + Encrypt(input []byte) ([]byte, error) + Decrypt(input []byte) ([]byte, error) + EncryptStream(input io.Reader) (io.Reader, error) + DecryptStream(input io.Reader) (io.Reader, error) +} + +func NewCryptor(algorithm CryptoAlgorithm) Cryptor { + var c Cryptor + if legacy, ok := algorithm.(*LegacyCryptoAlgorithm); ok { + c = newLegacyCryptor(legacy) + } else if extended, ok := algorithm.(ExtendedCryptoAlgorithm); ok { + c = newExtendedCryptorV1(extended) + } else { + c = newCryptorV1(algorithm.(CryptoAlgorithm)) + } + return c +} + +func newExtendedCryptorV1(algorithm ExtendedCryptoAlgorithm) Cryptor { + var c Cryptor + c = &extendedCryptorV1{ + cryptorV1: cryptorV1{ + algorithm: algorithm, + }, + algorithm: algorithm, + } + return c +} + +func newCryptorV1(algorithm CryptoAlgorithm) Cryptor { + var c Cryptor + c = &cryptorV1{ + algorithm: algorithm, + } + return c +} + +func newLegacyCryptor(algorithm ExtendedCryptoAlgorithm) Cryptor { + var c Cryptor + c = &legacyCryptor{ + algorithm: algorithm, + } + return c +} + +type extendedCryptorV1 struct { + cryptorV1 + algorithm ExtendedCryptoAlgorithm +} + +//func (c *extendedCryptorV1) Encrypt(message []byte) ([]byte, error) { +// return c.cryptorV1.Encrypt(message) +//} +// +//func (c *extendedCryptorV1) Decrypt(message []byte) ([]byte, error) { +// return c.cryptorV1.Decrypt(message) +//} + +func (c *extendedCryptorV1) EncryptStream(input io.Reader) (io.Reader, error) { + encryptedStreamData, e := c.algorithm.EncryptStream(input) + if e != nil { + return nil, e + } + header, e := headerV1(c.algorithm.Id(), encryptedStreamData.Metadata) + if e != nil { + return nil, e + } + + headerReader := bytes.NewReader(header) + + return io.MultiReader(headerReader, encryptedStreamData.Reader), nil +} + +func (c *extendedCryptorV1) DecryptStream(input io.Reader) (io.Reader, error) { + _, readerWithOnlyData, metadata, e := parseHeaderStream(input) + if e != nil { + return nil, e + } + return c.algorithm.DecryptStream(&EncryptedStreamData{Reader: readerWithOnlyData, Metadata: metadata}) +} + +type cryptorV1 struct { + algorithm CryptoAlgorithm +} + +func (c *cryptorV1) Id() string { + return c.algorithm.Id() +} + +func (c *cryptorV1) Encrypt(message []byte) ([]byte, error) { + encryptedData, e := c.algorithm.Encrypt(message) + if e != nil { + return nil, e + } + header, e := headerV1(c.algorithm.Id(), encryptedData.Metadata) + if e != nil { + return nil, e + } + + return append(header, encryptedData.Data...), nil +} + +func (c *cryptorV1) Decrypt(message []byte) ([]byte, error) { + _, encryptedData, e := parseHeader(message) + if e != nil { + return nil, e + } + return c.algorithm.Decrypt(encryptedData) +} + +func (c *cryptorV1) EncryptStream(input io.Reader) (io.Reader, error) { + inputBytes, e := io.ReadAll(input) + if e != nil { + return nil, e + } + + encryptedData, e := c.algorithm.Encrypt(inputBytes) + if e != nil { + return nil, e + } + + header, e := headerV1(c.algorithm.Id(), encryptedData.Metadata) + if e != nil { + return nil, e + } + + return io.MultiReader(bytes.NewReader(header), bytes.NewReader(encryptedData.Data)), nil +} + +func (c *cryptorV1) DecryptStream(input io.Reader) (io.Reader, error) { + inputBytes, e := io.ReadAll(input) + if e != nil { + return nil, e + } + + _, encryptedData, e := parseHeader(inputBytes) + if e != nil { + return nil, e + } + + decryptedData, e := c.algorithm.Decrypt(encryptedData) + if e != nil { + return nil, e + } + + return bytes.NewReader(decryptedData), nil +} + +type legacyCryptor struct { + algorithm ExtendedCryptoAlgorithm +} + +func (c *legacyCryptor) Id() string { + return c.algorithm.Id() +} + +func (c *legacyCryptor) Encrypt(message []byte) ([]byte, error) { + encryptedData, e := c.algorithm.Encrypt(message) + if e != nil { + return nil, e + } + + return encryptedData.Data, nil +} + +func (c *legacyCryptor) Decrypt(message []byte) ([]byte, error) { + return c.algorithm.Decrypt(&EncryptedData{Data: message, Metadata: nil}) +} + +func (c *legacyCryptor) EncryptStream(input io.Reader) (io.Reader, error) { + encryptedStreamData, e := c.algorithm.EncryptStream(input) + if e != nil { + return nil, e + } + + return encryptedStreamData.Reader, nil +} + +func (c *legacyCryptor) DecryptStream(input io.Reader) (io.Reader, error) { + return c.algorithm.DecryptStream(&EncryptedStreamData{Reader: input, Metadata: nil}) +} diff --git a/utils/cryptor_header.go b/crypto/cryptor_header.go similarity index 57% rename from utils/cryptor_header.go rename to crypto/cryptor_header.go index a729f667..d585455f 100644 --- a/utils/cryptor_header.go +++ b/crypto/cryptor_header.go @@ -1,4 +1,4 @@ -package utils +package crypto import ( "bufio" @@ -28,8 +28,8 @@ const sentinelLength = 4 var sentinel = [sentinelLength]byte{0x50, 0x4E, 0x45, 0x44} -func returnWithV1Header(cryptorId []byte, encryptedData EncryptedData) ([]byte, error) { - cryptorDataSize := len(encryptedData.Metadata) +func headerV1(cryptorId string, metadata []byte) ([]byte, error) { + cryptorDataSize := len(metadata) var cryptorDataBytesSize int if cryptorDataSize <= maxShortSize { @@ -37,21 +37,41 @@ func returnWithV1Header(cryptorId []byte, encryptedData EncryptedData) ([]byte, } else { cryptorDataBytesSize = longSizeLength } - r := make([]byte, 0, len(sentinel)+1+cryptorIdLength+cryptorDataBytesSize+cryptorDataSize+len(encryptedData.Data)) + r := make([]byte, 0, len(sentinel)+1+cryptorIdLength+cryptorDataBytesSize+cryptorDataSize) buffer := bytes.NewBuffer(r) - buffer.Write(sentinel[:]) - buffer.WriteByte(versionV1) - buffer.Write(cryptorId) + _, e := buffer.Write(sentinel[:]) + if e != nil { + return nil, e + } + e = buffer.WriteByte(versionV1) + if e != nil { + return nil, e + } + + _, e = buffer.Write([]byte(cryptorId)) + if e != nil { + return nil, e + } if cryptorDataBytesSize == shortSizeLength { - buffer.WriteByte(byte(cryptorDataSize)) + e = buffer.WriteByte(byte(cryptorDataSize)) + if e != nil { + return nil, e + } } else { - buffer.WriteByte(longSizeIndicator) - buffer.Write([]byte(strconv.FormatInt(int64(cryptorDataSize), 10))) + e = buffer.WriteByte(longSizeIndicator) + if e != nil { + return nil, e + } + _, e = buffer.Write([]byte(strconv.FormatInt(int64(cryptorDataSize), 10))) + if e != nil { + return nil, e + } + } + _, e = buffer.Write(metadata) + if e != nil { + return nil, e } - buffer.Write(encryptedData.Metadata) - buffer.Write(encryptedData.Data) - return buffer.Bytes(), nil } @@ -67,7 +87,20 @@ func slicesEqual(a, b []byte) bool { return true } -func ParseHeader(data []byte) ([]byte, *EncryptedData, error) { +func peekHeaderCryptorId(data []byte) (cryptorId *string, e error) { + if !slicesEqual(data[:len(sentinel)], sentinel[:]) { + return nil, nil + } + + if data[versionPosition] != versionV1 { + return nil, errors.New("unsupported crypto header version") + } + + id := string(data[cryptorIdPosition : cryptorIdPosition+cryptorIdLength]) + return &id, nil +} + +func parseHeader(data []byte) (cryptorId *string, encryptedData *EncryptedData, e error) { if !slicesEqual(data[:len(sentinel)], sentinel[:]) { return nil, &EncryptedData{Metadata: nil, Data: data}, nil } @@ -76,7 +109,7 @@ func ParseHeader(data []byte) ([]byte, *EncryptedData, error) { return nil, nil, errors.New("unsupported crypto header version") } - cryptorId := data[cryptorIdPosition : cryptorIdPosition+cryptorIdLength] + id := string(data[cryptorIdPosition : cryptorIdPosition+cryptorIdLength]) var headerSize int64 position := int64(sizePosition) if data[sizePosition] == longSizeIndicator { @@ -94,10 +127,28 @@ func ParseHeader(data []byte) ([]byte, *EncryptedData, error) { metadata := data[position : position+headerSize] position += int64(len(metadata)) - return cryptorId, &EncryptedData{Data: data[position:], Metadata: metadata}, nil + return &id, &EncryptedData{Data: data[position:], Metadata: metadata}, nil +} + +func peekHeaderStreamCryptorId(data *bufio.Reader) (cryptorId *string, e error) { + peeked, err := data.Peek(sentinelLength + 1 + cryptorIdLength + longSizeLength) + if err != nil { + return nil, err + } + + if !slicesEqual(peeked[:len(sentinel)], sentinel[:]) { + return nil, nil + } + + if peeked[versionPosition] != versionV1 { + return nil, errors.New("unsupported crypto header version") + } + + id := string(peeked[cryptorIdPosition : cryptorIdPosition+cryptorIdLength]) + return &id, nil } -func ParseHeaderStream(data io.Reader) ([]byte, io.Reader, []byte, error) { +func parseHeaderStream(data io.Reader) (cryptorId []byte, d io.Reader, metadata []byte, e error) { bufData := bufio.NewReader(data) peeked, err := bufData.Peek(sentinelLength + 1 + cryptorIdLength + longSizeLength) @@ -113,7 +164,7 @@ func ParseHeaderStream(data io.Reader) ([]byte, io.Reader, []byte, error) { return nil, nil, nil, errors.New("unsupported crypto header version") } - cryptorId := peeked[cryptorIdPosition : cryptorIdPosition+cryptorIdLength] + id := peeked[cryptorIdPosition : cryptorIdPosition+cryptorIdLength] var headerSize int64 position := int64(sizePosition) if peeked[sizePosition] == longSizeIndicator { @@ -138,10 +189,10 @@ func ParseHeaderStream(data io.Reader) ([]byte, io.Reader, []byte, error) { return nil, nil, nil, e } } - metadata := make([]byte, headerSize) - _, e := io.ReadFull(bufData, metadata) + m := make([]byte, headerSize) + _, e = io.ReadFull(bufData, metadata) if e != nil { return nil, nil, nil, e } - return cryptorId, bufData, metadata, nil + return id, bufData, m, nil } diff --git a/crypto/decrypting_reader.go b/crypto/decrypting_reader.go new file mode 100644 index 00000000..41d9faa4 --- /dev/null +++ b/crypto/decrypting_reader.go @@ -0,0 +1,134 @@ +package crypto + +import ( + "bufio" + "bytes" + "crypto/cipher" + "errors" + "io" +) + +func NewBlockModeDecryptingReader(r io.Reader, mode cipher.BlockMode) io.Reader { + return &BlockModeDecryptingReader{ + r: bufio.NewReader(r), + blockMode: mode, + buffer: bytes.NewBuffer(nil), + err: nil, + } +} + +type BlockModeDecryptingReader struct { + r *bufio.Reader + blockMode cipher.BlockMode + buffer *bytes.Buffer + err error +} + +func (decryptingReader *BlockModeDecryptingReader) readNextBlock() ([]byte, error) { + reader := decryptingReader.r + + output := make([]byte, decryptingReader.blockMode.BlockSize()) + sizeOfCurrentlyRead, readErr := io.ReadFull(reader, output) + if readErr != nil && readErr != io.EOF { + return output[:sizeOfCurrentlyRead], readErr + } + + if sizeOfCurrentlyRead == 0 && readErr == io.EOF { + return nil, io.EOF + } + + if _, e := reader.Peek(1); e == io.EOF { + return output[:sizeOfCurrentlyRead], io.EOF + } + + return output, nil +} + +func (decryptingReader *BlockModeDecryptingReader) decryptUntilPFull(p []byte) (n int, err error) { + var copied int + var block []byte + var e error + alreadyWrote := 0 + for alreadyWrote < len(p) { + block, e = decryptingReader.readNextBlock() + decryptingReader.err = e + + if errors.Is(e, io.ErrUnexpectedEOF) { + return alreadyWrote, errors.New("encrypted data length is not a multiple of the block size") + } + + if e != nil && e != io.EOF { + return alreadyWrote, e + } + + decryptingReader.blockMode.CryptBlocks(block, block) + + if bytes.Equal(block, bytes.Repeat([]byte{byte(decryptingReader.blockMode.BlockSize())}, len(block))) { + break + } else if e == io.EOF { + unpadded, unpadErr := unpadPKCS7(block) + if len(unpadded) > 0 { + block = unpadded + copied = copy(p[alreadyWrote:], block) + alreadyWrote += copied + } + + if unpadErr != nil { + return alreadyWrote, unpadErr + } + if copied < len(block) { + decryptingReader.buffer.Write(block[copied:]) + } + break + } else { + copied = copy(p[alreadyWrote:], block) + alreadyWrote += copied + if copied < len(block) { + decryptingReader.buffer.Write(block[copied:]) + } + } + } + + return alreadyWrote, nil +} + +func readFromBufferUntilEmpty(buffer *bytes.Buffer, p []byte) int { + buffered := buffer.Next(len(p)) + if len(buffered) == 0 { + return 0 + } + return copy(p, buffered) +} + +func (decryptingReader *BlockModeDecryptingReader) readFromBufferUntilEmpty(p []byte) (n int, err error) { + buffered := decryptingReader.buffer.Next(len(p)) + var alreadyWrote int + if len(buffered) > 0 { + alreadyWrote = copy(p, buffered) + } + + if decryptingReader.err != nil { + return alreadyWrote, decryptingReader.err + } + + return alreadyWrote, nil +} + +func (decryptingReader *BlockModeDecryptingReader) Read(p []byte) (n int, err error) { + if len(p) == 0 { + return 0, errors.New("cannot read into empty buffer") + } + + if (decryptingReader.err != nil) && decryptingReader.buffer.Len() == 0 { + return 0, decryptingReader.err + } + + alreadyWrote := readFromBufferUntilEmpty(decryptingReader.buffer, p) + + if decryptingReader.err != nil { + return alreadyWrote, nil + } + + n, e := decryptingReader.decryptUntilPFull(p[alreadyWrote:]) + return alreadyWrote + n, e +} diff --git a/crypto/decrypting_reader_test.go b/crypto/decrypting_reader_test.go new file mode 100644 index 00000000..40d07463 --- /dev/null +++ b/crypto/decrypting_reader_test.go @@ -0,0 +1,49 @@ +package crypto + +import ( + "bytes" + "testing" + "testing/quick" +) + +type DoNothingBlockMode struct { +} + +func (d *DoNothingBlockMode) BlockSize() int { + return 16 +} + +func (d *DoNothingBlockMode) CryptBlocks(dst, src []byte) { + copy(dst, src) +} + +func canReadDifferentSizeOfChunks(in []byte, bufferSize uint8) bool { + inPadded := padWithPKCS7VarBlock(in, 16) + if bufferSize == 0 { + return true + } + decryptingReader := NewBlockModeDecryptingReader(bytes.NewReader(inPadded), &DoNothingBlockMode{}) + readDataBuffer := bytes.NewBuffer(nil) + buffer := make([]byte, bufferSize) + numberOfReadBytes := 0 + + var e error + var readBytes int + + for e == nil { + readBytes, e = decryptingReader.Read(buffer) + numberOfReadBytes += readBytes + readDataBuffer.Write(buffer[:readBytes]) + } + + out := readDataBuffer.Bytes()[:numberOfReadBytes] + + return bytes.Equal(in, out) +} + +func Test_DecryptingReader_ReadDifferentSizeOfBuffers(t *testing.T) { + c := quick.Config{MaxCount: 10000} + if err := quick.Check(canReadDifferentSizeOfChunks, &c); err != nil { + t.Error(err) + } +} diff --git a/crypto/encrypting_reader.go b/crypto/encrypting_reader.go new file mode 100644 index 00000000..523121b8 --- /dev/null +++ b/crypto/encrypting_reader.go @@ -0,0 +1,86 @@ +package crypto + +import ( + "bufio" + "bytes" + "crypto/cipher" + "errors" + "io" +) + +func NewBlockModeEncryptingReader(r io.Reader, mode cipher.BlockMode) io.Reader { + return &BlockModeEncryptingReader{ + r: bufio.NewReader(r), + blockMode: mode, + buffer: bytes.NewBuffer(nil), + err: nil, + } +} + +type BlockModeEncryptingReader struct { + r *bufio.Reader + blockMode cipher.BlockMode + buffer *bytes.Buffer + err error +} + +func (encryptingReader *BlockModeEncryptingReader) readNextBlockPadded() (read []byte, err error) { + output := make([]byte, encryptingReader.blockMode.BlockSize()) + sizeOfCurrentlyRead, readErr := io.ReadFull(encryptingReader.r, output) + if readErr != nil && readErr != io.EOF && !errors.Is(readErr, io.ErrUnexpectedEOF) { + return nil, readErr + } + + if sizeOfCurrentlyRead == 0 && readErr == io.EOF { + return padWithPKCS7VarBlock(nil, encryptingReader.blockMode.BlockSize()), io.EOF + } + + if errors.Is(readErr, io.ErrUnexpectedEOF) { + return padWithPKCS7VarBlock(output[:sizeOfCurrentlyRead], encryptingReader.blockMode.BlockSize()), io.EOF + } + + return output, nil +} + +func (encryptingReader *BlockModeEncryptingReader) encryptUntilPFull(p []byte) (int, error) { + var alreadyWrote int + for alreadyWrote <= len(p) { + block, e := encryptingReader.readNextBlockPadded() + if e != nil && e != io.EOF { + return alreadyWrote, e + } + encryptingReader.err = e + + encryptingReader.blockMode.CryptBlocks(block, block) + copied := copy(p[alreadyWrote:], block) + alreadyWrote += copied + if copied < len(block) { + encryptingReader.buffer.Write(block[copied:]) + } + if e == io.EOF { + break + } + } + + return alreadyWrote, nil +} + +func (encryptingReader *BlockModeEncryptingReader) Read(p []byte) (n int, err error) { + if len(p) == 0 { + return 0, errors.New("cannot read into empty buffer") + } + + if (encryptingReader.err != nil) && encryptingReader.buffer.Len() == 0 { + return 0, encryptingReader.err + } + + alreadyWrote := readFromBufferUntilEmpty(encryptingReader.buffer, p) + + if encryptingReader.err != nil { + return alreadyWrote, nil + } + + n, e := encryptingReader.encryptUntilPFull(p[alreadyWrote:]) + + return alreadyWrote + n, e +} diff --git a/crypto/encrypting_reader_test.go b/crypto/encrypting_reader_test.go new file mode 100644 index 00000000..56cfcb6e --- /dev/null +++ b/crypto/encrypting_reader_test.go @@ -0,0 +1,43 @@ +package crypto + +import ( + "bytes" + "github.com/stretchr/testify/assert" + "testing" + "testing/quick" +) + +func encryptingReaderCanReadDifferentSizeOfChunks(in []byte, bufferSize uint8) bool { + inPadded := padWithPKCS7VarBlock(in, 16) + if bufferSize == 0 { + return true + } + encryptingReader := NewBlockModeEncryptingReader(bytes.NewReader(in), &DoNothingBlockMode{}) + readDataBuffer := bytes.NewBuffer(nil) + buffer := make([]byte, bufferSize) + numberOfReadBytes := 0 + + var e error + var readBytes int + + for e == nil { + readBytes, e = encryptingReader.Read(buffer) + numberOfReadBytes += readBytes + readDataBuffer.Write(buffer[:readBytes]) + } + + out := readDataBuffer.Bytes()[:numberOfReadBytes] + + return bytes.Equal(inPadded, out) +} + +func Test_EncryptingReader_ReadDifferentSizeOfBuffers(t *testing.T) { + c := quick.Config{MaxCount: 10000} + if err := quick.Check(encryptingReaderCanReadDifferentSizeOfChunks, &c); err != nil { + t.Error(err) + } +} + +func Test_EncryptingReader_ReadDifferentSizeOfBuffers1(t *testing.T) { + assert.True(t, encryptingReaderCanReadDifferentSizeOfChunks([]byte{0x25, 0xb4, 0x40, 0x82, 0x6c, 0xbf, 0x30, 0xd1, 0xad, 0x8e, 0x31, 0x4f, 0x72, 0xd5, 0xbb, 0xd3, 0xc7, 0x2c, 0xf1, 0x60, 0x68, 0x76, 0x98, 0xda, 0x36, 0x3d, 0xe5, 0xc0, 0xfd, 0xd1, 0x57, 0x44, 0xca, 0xcf, 0xd3, 0xd1}, 0x21)) +} diff --git a/utils/legacy_crypto_algorithm.go b/crypto/legacy_crypto_algorithm.go similarity index 50% rename from utils/legacy_crypto_algorithm.go rename to crypto/legacy_crypto_algorithm.go index ceb648f3..0f642ff0 100644 --- a/utils/legacy_crypto_algorithm.go +++ b/crypto/legacy_crypto_algorithm.go @@ -1,19 +1,24 @@ -package utils +package crypto import ( + "bytes" "crypto/aes" "crypto/cipher" "crypto/sha256" "encoding/hex" + "fmt" "io" ) +// 16 byte constant legacy IV +var valIV = "0123456789012345" + type LegacyCryptoAlgorithm struct { block cipher.Block useRandomIvForSlices bool } -func NewLegacyCryptor(cipherKey string, useRandomIV bool) (*LegacyCryptoAlgorithm, error) { +func NewLegacyCryptoAlgorithm(cipherKey string, useRandomIV bool) (*LegacyCryptoAlgorithm, error) { block, e := legacyAesCipher(cipherKey) if e != nil { return nil, e @@ -25,70 +30,73 @@ func NewLegacyCryptor(cipherKey string, useRandomIV bool) (*LegacyCryptoAlgorith }, nil } -var legacyId = []byte{0x00, 0x00, 0x00, 0x00} +var legacyId = "1234" -func (c *LegacyCryptoAlgorithm) Id() []byte { +func (c *LegacyCryptoAlgorithm) Id() string { return legacyId } -func (c *LegacyCryptoAlgorithm) HeaderVersion() CryptoHeaderVersion { - return headless -} - -func (c *LegacyCryptoAlgorithm) Encrypt(value []byte) (EncryptedData, error) { - value = padWithPKCS7(value) +func (c *LegacyCryptoAlgorithm) Encrypt(message []byte) (*EncryptedData, error) { + message = padWithPKCS7(message) iv := make([]byte, aes.BlockSize) if c.useRandomIvForSlices { iv = generateIV(aes.BlockSize) } else { iv = []byte(valIV) } - blockmode := cipher.NewCBCEncrypter(c.block, iv) - cipherBytes := encrypt(blockmode, value) + + encryptedBytes := make([]byte, len(message)) + blockmode.CryptBlocks(encryptedBytes, message) if c.useRandomIvForSlices { - return EncryptedData{Data: append(iv, cipherBytes...), Metadata: nil}, nil + return &EncryptedData{Data: append(iv, encryptedBytes...), Metadata: nil}, nil } - return EncryptedData{Data: cipherBytes, Metadata: iv}, nil + return &EncryptedData{Data: encryptedBytes, Metadata: nil}, nil } -func (c *LegacyCryptoAlgorithm) Decrypt(encryptedData EncryptedData) ([]byte, error) { +func (c *LegacyCryptoAlgorithm) Decrypt(encryptedData *EncryptedData) (r []byte, e error) { iv := make([]byte, aes.BlockSize) if c.useRandomIvForSlices { - iv = generateIV(aes.BlockSize) //TODO + iv = generateIV(aes.BlockSize) } else { iv = []byte(valIV) } - decrypter := cipher.NewCBCDecrypter(c.block, iv) + //to handle decryption errors + defer func() { + if rec := recover(); rec != nil { + r, e = nil, fmt.Errorf("decrypt error: %s", rec) + } + }() + + decrypted := make([]byte, len(encryptedData.Data)) + decrypter.CryptBlocks(decrypted, encryptedData.Data) + val, err := unpadPKCS7(decrypted) + if err != nil { + return nil, fmt.Errorf("decrypt error: %s", err) + } - return decrypt(decrypter, encryptedData.Data) + return val, nil } -func (c *LegacyCryptoAlgorithm) EncryptStream(input io.Reader, output io.Writer) ([]byte, error) { +func (c *LegacyCryptoAlgorithm) EncryptStream(reader io.Reader) (*EncryptedStreamData, error) { iv := generateIV(aes.BlockSize) - _, err := output.Write(iv) - if err != nil { - return nil, err - } - blockmode := cipher.NewCBCEncrypter(c.block, iv) - err = encryptStream(blockmode, input, output) - if err != nil { - return nil, err - } - return nil, nil + + return &EncryptedStreamData{ + Metadata: nil, + Reader: io.MultiReader(bytes.NewReader(iv), NewBlockModeEncryptingReader(reader, cipher.NewCBCEncrypter(c.block, iv))), + }, nil } -func (c *LegacyCryptoAlgorithm) DecryptStream(input io.Reader, _ []byte, output io.Writer) error { +func (c *LegacyCryptoAlgorithm) DecryptStream(encryptedData *EncryptedStreamData) (io.Reader, error) { iv := make([]byte, aes.BlockSize) - _, err := io.ReadFull(input, iv) + _, err := io.ReadFull(encryptedData.Reader, iv) if err != nil { - return nil + return nil, err } - blockmode := cipher.NewCBCEncrypter(c.block, iv) - return decryptStream(blockmode, input, output) + return NewBlockModeDecryptingReader(encryptedData.Reader, cipher.NewCBCDecrypter(c.block, iv)), nil } // EncryptCipherKey DEPRECATED diff --git a/crypto/legacy_crypto_algorithm_test.go b/crypto/legacy_crypto_algorithm_test.go new file mode 100644 index 00000000..74108de6 --- /dev/null +++ b/crypto/legacy_crypto_algorithm_test.go @@ -0,0 +1,75 @@ +package crypto + +import ( + "bytes" + "io" + "testing" + "testing/quick" +) + +func legacyCanDecryptEncryptStreamResult(in []byte) bool { + cryptoAlgorithm, e := NewLegacyCryptoAlgorithm("enigma", true) + if e != nil { + return false + } + output, err := cryptoAlgorithm.EncryptStream(bytes.NewReader(in)) + if err != nil { + return false + } + + encryptedData, err := io.ReadAll(output.Reader) + + decrypted, err := cryptoAlgorithm.Decrypt(&EncryptedData{ + Data: encryptedData, + Metadata: output.Metadata, + }) + if err != nil { + return false + } + return bytes.Equal(in, decrypted[16:]) +} + +func legacyCanDecryptStreamEncryptResult(in []byte) bool { + cryptor, e := NewLegacyCryptoAlgorithm("enigma", true) + if e != nil { + return false + } + + output, err := cryptor.Encrypt(in) + if err != nil { + println(err.Error()) + return false + } + + decryptingReader, err := cryptor.DecryptStream(&EncryptedStreamData{ + Reader: bytes.NewReader(output.Data), + Metadata: output.Metadata, + }) + if err != nil { + println(err.Error()) + return false + } + + decrypted, err := io.ReadAll(decryptingReader) + if err != nil { + println(err.Error()) + return false + } + + return bytes.Equal(in, decrypted) +} + +func Test_Legacy_EncryptStream(t *testing.T) { + c := quick.Config{MaxCount: 10000} + + if err := quick.Check(legacyCanDecryptEncryptStreamResult, &c); err != nil { + t.Error(err) + } +} + +func Test_Legacy_DecryptStream(t *testing.T) { + c := quick.Config{MaxCount: 10000} + if err := quick.Check(legacyCanDecryptStreamEncryptResult, &c); err != nil { + t.Error(err) + } +} diff --git a/crypto/utils.go b/crypto/utils.go new file mode 100644 index 00000000..c4b42e75 --- /dev/null +++ b/crypto/utils.go @@ -0,0 +1,57 @@ +package crypto + +import ( + "bytes" + "crypto/rand" + "fmt" +) + +func generateIV(blocksize int) []byte { + iv := make([]byte, blocksize) + if _, err := rand.Read(iv); err != nil { + panic(err) + } + return iv +} + +func padWithPKCS7VarBlock(data []byte, blocklen int) []byte { + padlen := 1 + for ((len(data) + padlen) % blocklen) != 0 { + padlen = padlen + 1 + } + + pad := bytes.Repeat([]byte{byte(padlen)}, padlen) + return append(data, pad...) +} + +// padWithPKCS7 pads the data as per the PKCS7 standard +// It accepts the following parameters: +// data: data to pad as byte array. +// returns the padded data as byte array. +func padWithPKCS7(data []byte) []byte { + return padWithPKCS7VarBlock(data, 16) +} + +// unpadPKCS7 unpads the data as per the PKCS7 standard +// It accepts the following parameters: +// data: data to unpad as byte array. +// returns the unpadded data as byte array. +func unpadPKCS7(data []byte) ([]byte, error) { + blocklen := 16 + if len(data)%blocklen != 0 || len(data) == 0 { + return nil, fmt.Errorf("invalid data len %d", len(data)) + } + padlen := int(data[len(data)-1]) + if padlen > blocklen || padlen == 0 { + return nil, fmt.Errorf("padding is invalid") + } + // check padding + pad := data[len(data)-padlen:] + for i := 0; i < padlen; i++ { + if pad[i] != byte(padlen) { + return nil, fmt.Errorf("padding is invalid") + } + } + + return data[:len(data)-padlen], nil +} diff --git a/utils/aes_cbc_crypto_algorithm_test.go b/utils/aes_cbc_crypto_algorithm_test.go deleted file mode 100644 index 95ed13bd..00000000 --- a/utils/aes_cbc_crypto_algorithm_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package utils - -import ( - "bytes" - "github.com/stretchr/testify/assert" - "strings" - "testing" - "testing/quick" -) - -func doesEncryptItWork(in []byte) bool { - cryptor, e := NewCBCCryptor("enigma") - if e != nil { - return false - } - - outputBuff := bytes.NewBuffer(make([]byte, 0, len(in))) - metadata, err := cryptor.EncryptStream(bytes.NewReader(in), outputBuff) - if err != nil { - println(err.Error()) - return false - } - - decrypted, err := cryptor.Decrypt(EncryptedData{ - Data: outputBuff.Bytes(), - Metadata: metadata, - }) - if err != nil { - println(err.Error()) - return false - } - return bytes.Equal(in, decrypted) -} - -func doesDecryptItWork(in []byte) bool { - cryptor, e := NewCBCCryptor("enigma") - if e != nil { - return false - } - - outputBuff := bytes.NewBuffer(make([]byte, 0, len(in))) - metadata, err := cryptor.EncryptStream(bytes.NewReader(in), outputBuff) - if err != nil { - println(err.Error()) - return false - } - - decrypted, err := cryptor.Decrypt(EncryptedData{ - Data: outputBuff.Bytes(), - Metadata: metadata, - }) - if err != nil { - println(err.Error()) - return false - } - return bytes.Equal(in, decrypted) -} - -func TestEncryptStreamWorks(t *testing.T) { - if err := quick.Check(doesEncryptItWork, nil); err != nil { - t.Error(err) - } -} - -func TestDecryptStreamWorks(t *testing.T) { - if err := quick.Check(doesDecryptItWork, nil); err != nil { - t.Error(err) - } -} - -func TestTest(t *testing.T) { - assert.True(t, doesEncryptItWork([]byte{0x96, 0x8f, 0xc6, 0x16, 0x7, 0x60, 0x8, 0xd2, 0xcc, 0x3e, 0xe1, 0x1a, 0x40, 0xbe, 0x75, 0xa9, 0x91, 0x82, 0x40, 0x5d, 0x92, 0xa2, 0x68, 0xd4, 0x68, 0x7, 0xfd, 0x43, 0x7, 0x9b, 0x2d, 0x77, 0x2e, 0xaa, 0xc7, 0x18, 0xe1, 0xb7})) -} - -func TestCBCCryptor_EncryptStream(t *testing.T) { - cryptor, e := NewCBCCryptor("enigma") - if e != nil { - t.Errorf("error, %s", e) - } - - message := "sure i can" - buff := bytes.NewBuffer(make([]byte, 0, len(message))) - metadata, err := cryptor.EncryptStream(strings.NewReader(message), buff) - if err != nil { - t.Errorf("error, %s", err) - } - - decrypted, err := cryptor.Decrypt(EncryptedData{Data: buff.Bytes(), Metadata: metadata}) - if err != nil { - t.Errorf("error, %s", err) - } - - a := assert.New(t) - a.Equal(message, string(decrypted)) -} - -func TestCBCCryptor_DecryptStream(t *testing.T) { - cryptor, e := NewCBCCryptor("enigma") - if e != nil { - t.Errorf("error, %s", e) - } - - message := "sure i can" - buff := bytes.NewBuffer(make([]byte, 0, len(message))) - metadata, err := cryptor.EncryptStream(strings.NewReader(message), buff) - if err != nil { - t.Errorf("error, %s", err) - } - - decryptBuff := bytes.NewBuffer(make([]byte, 0, len(message))) - - err2 := cryptor.DecryptStream(bytes.NewReader(buff.Bytes()), metadata, decryptBuff) - if err2 != nil { - t.Errorf("error, %s", err2) - } - - a := assert.New(t) - a.Equal(message, decryptBuff.String()) -} diff --git a/utils/crypto.go b/utils/crypto.go index eb759f36..e9875b82 100644 --- a/utils/crypto.go +++ b/utils/crypto.go @@ -6,15 +6,13 @@ import ( "crypto/sha256" "encoding/base64" "fmt" + "github.com/pubnub/go/v7/crypto" "io" "os" "strconv" "strings" ) -// 16 byte IV -var valIV = "0123456789012345" - // EncryptString DEPRECATED // EncryptString creates the base64 encoded encrypted string using the // cipherKey. @@ -25,15 +23,15 @@ var valIV = "0123456789012345" // // returns the base64 encoded encrypted string. func EncryptString(cipherKey string, message string, useRandomInitializationVector bool) string { - cryptor, e := NewLegacyCryptor(cipherKey, useRandomInitializationVector) + cryptoModule, e := crypto.NewLegacyCryptoModule(cipherKey, useRandomInitializationVector) if e != nil { panic(e) } - encryptedData, e := cryptor.Encrypt([]byte(encodeNonASCIIChars(message))) + encryptedData, e := cryptoModule.Encrypt([]byte(encodeNonASCIIChars(message))) if e != nil { panic(e) } - return base64.StdEncoding.EncodeToString(encryptedData.Data) + return base64.StdEncoding.EncodeToString(encryptedData) } // DecryptString DEPRECATED @@ -52,11 +50,11 @@ func DecryptString(cipherKey string, message string, useRandomInitializationVect return "***decrypt error***", fmt.Errorf("decrypt error on decode: %s", decodeErr) } - cryptor, e := NewLegacyCryptor(cipherKey, useRandomInitializationVector) + cryptoModule, e := crypto.NewLegacyCryptoModule(cipherKey, useRandomInitializationVector) if e != nil { return nil, e } - return cryptor.Decrypt(EncryptedData{Data: value, Metadata: nil}) + return cryptoModule.Decrypt(value) } // encodeNonAsciiChars creates unicode string of the non-ascii chars. @@ -102,11 +100,15 @@ func GetHmacSha256(secretKey string, input string) string { // EncryptFile DEPRECATED func EncryptFile(cipherKey string, _ []byte, filePart io.Writer, file *os.File) { - cryptor, e := NewLegacyCryptor(cipherKey, true) + cryptor, e := crypto.NewLegacyCryptoAlgorithm(cipherKey, true) + if e != nil { + panic(e) + } + r, e := cryptor.EncryptStream(file) if e != nil { panic(e) } - _, e = cryptor.EncryptStream(file, filePart) + _, e = io.Copy(filePart, r.Reader) if e != nil { panic(e) } @@ -114,13 +116,29 @@ func EncryptFile(cipherKey string, _ []byte, filePart io.Writer, file *os.File) // DecryptFile DEPRECATED func DecryptFile(cipherKey string, _ int64, reader io.Reader, w io.WriteCloser) { - cryptor, e := NewLegacyCryptor(cipherKey, true) + cryptoModule, e := crypto.NewLegacyCryptoModule(cipherKey, true) + if e != nil { + panic(e) + } + encryptedReader, e := cryptoModule.DecryptStream(reader) if e != nil { panic(e) } - e = cryptor.DecryptStream(reader, nil, w) + _, e = io.Copy(w, encryptedReader) if e != nil { panic(e) } e = w.Close() } + +// EncryptCipherKey DEPRECATED +// EncryptCipherKey creates the 256 bit hex of the cipher key +// +// It accepts the following parameters: +// cipherKey: cipher key to use to decrypt. +// +// returns the 256 bit hex of the cipher key. + +func EncryptCipherKey(cipherKey string) []byte { + return crypto.EncryptCipherKey(cipherKey) +} diff --git a/utils/crypto_algorithm.go b/utils/crypto_algorithm.go deleted file mode 100644 index e1493dca..00000000 --- a/utils/crypto_algorithm.go +++ /dev/null @@ -1,45 +0,0 @@ -package utils - -import ( - "bytes" - "io" -) - -type CryptoAlgorithm interface { - HeaderVersion() CryptoHeaderVersion - Id() []byte - Encrypt(message []byte) (EncryptedData, error) - Decrypt(encryptedData EncryptedData) ([]byte, error) - - EncryptStream(input io.Reader, output io.Writer) ([]byte, error) - DecryptStream(input io.Reader, metadata []byte, output io.Writer) error -} - -type EncryptedData struct { - Metadata []byte - Data []byte -} - -type EncryptingWriter interface { - io.Writer - WriteMetadata(in []byte) (n int, err error) -} - -type EncryptingWriterV1 struct { - w *io.Writer - metadataBuffer *bytes.Buffer - metadataDone bool -} - -func (e *EncryptingWriterV1) Write(in []byte) (n int, err error) { - e.metadataDone = true - return 0, nil -} - -func (e *EncryptingWriterV1) WriteMetadata(in []byte) error { - _, er := e.metadataBuffer.Write(in) - if er != nil { - return er - } - return nil -} diff --git a/utils/crypto_cbc.go b/utils/crypto_cbc.go deleted file mode 100644 index 07754605..00000000 --- a/utils/crypto_cbc.go +++ /dev/null @@ -1,134 +0,0 @@ -package utils - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "errors" - "fmt" - "io" -) - -func encrypt(mode cipher.BlockMode, input []byte) []byte { - cipherBytes := make([]byte, len(input)) - mode.CryptBlocks(cipherBytes, input) - return cipherBytes -} - -func decrypt(mode cipher.BlockMode, input []byte) (output []byte, err error) { - //to handle decryption errors - defer func() { - if r := recover(); r != nil { - output, err = nil, fmt.Errorf("decrypt error: %s", r) - } - }() - decrypted := make([]byte, len(input)) - mode.CryptBlocks(decrypted, input) - val, err := unpadPKCS7(decrypted) - if err != nil { - return nil, fmt.Errorf("decrypt error: %s", err) - } - - return val, nil -} - -func encryptStream(mode cipher.BlockMode, input io.Reader, output io.Writer) error { - readDataSlice := make([]byte, aes.BlockSize) - for { - sizeOfCurrentlyRead, readErr := io.ReadFull(input, readDataSlice) - - if readErr != nil && readErr != io.EOF && !errors.Is(readErr, io.ErrUnexpectedEOF) { - return readErr - } - - if sizeOfCurrentlyRead < aes.BlockSize { - pad := bytes.Repeat([]byte{byte(aes.BlockSize - sizeOfCurrentlyRead)}, aes.BlockSize-sizeOfCurrentlyRead) - copy(readDataSlice[sizeOfCurrentlyRead:], pad) - } - - mode.CryptBlocks(readDataSlice, readDataSlice) - _, writeErr := output.Write(readDataSlice) - if writeErr != nil { - return writeErr - } - - if readErr == io.EOF || errors.Is(readErr, io.ErrUnexpectedEOF) { - break - } - - } - - return nil -} - -func decryptStream(mode cipher.BlockMode, input io.Reader, output io.Writer) error { - readDataSlice := make([]byte, aes.BlockSize) - - for { - _, readErr := io.ReadFull(input, readDataSlice) - - if readErr != nil && readErr != io.EOF { - return readErr - } - - if readErr == io.EOF { - break - } - - mode.CryptBlocks(readDataSlice, readDataSlice) - unpadded, unpadError := unpadPKCS7(readDataSlice) - if unpadError != nil { - return unpadError - } - _, writeErr := output.Write(unpadded) - if writeErr != nil { - return writeErr - } - - if errors.Is(readErr, io.ErrUnexpectedEOF) { - break - } - - } - - return nil -} - -// padWithPKCS7 pads the data as per the PKCS7 standard -// It accepts the following parameters: -// data: data to pad as byte array. -// returns the padded data as byte array. -func padWithPKCS7(data []byte) []byte { - blocklen := 16 - padlen := 1 - for ((len(data) + padlen) % blocklen) != 0 { - padlen = padlen + 1 - } - - pad := bytes.Repeat([]byte{byte(padlen)}, padlen) - return append(data, pad...) -} - -// unpadPKCS7 unpads the data as per the PKCS7 standard -// It accepts the following parameters: -// data: data to unpad as byte array. -// returns the unpadded data as byte array. -func unpadPKCS7(data []byte) ([]byte, error) { - blocklen := 16 - if len(data)%blocklen != 0 || len(data) == 0 { - return nil, fmt.Errorf("invalid data len %d", len(data)) - } - padlen := int(data[len(data)-1]) - if padlen > blocklen || padlen == 0 { - return nil, fmt.Errorf("padding is invalid") - } - // check padding - pad := data[len(data)-padlen:] - for i := 0; i < padlen; i++ { - if pad[i] != byte(padlen) { - return nil, fmt.Errorf("padding is invalid") - } - } - - return data[:len(data)-padlen], nil -} diff --git a/utils/crypto_cbc_test.go b/utils/crypto_cbc_test.go deleted file mode 100644 index ca6d342b..00000000 --- a/utils/crypto_cbc_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package utils - -import "testing" - -func TestUnnpad(t *testing.T) { - in := []byte{0xbe, 0x77, 0x44, 0xee, 0xcd, 0xc8, 0x5f, 0xf, 0xec, 0x20, 0x6c, 0x2d, 0x86, 0x7c, 0x23, 0xeb} - pkcs7, err := unpadPKCS7(in) - if err != nil { - t.Errorf("error, %s", err) - } - println(string(pkcs7)) -} diff --git a/utils/new_crypto.go b/utils/new_crypto.go deleted file mode 100644 index 6848a19e..00000000 --- a/utils/new_crypto.go +++ /dev/null @@ -1,77 +0,0 @@ -package utils - -import ( - "errors" - "io" -) - -type Crypto struct { - encryptor CryptoAlgorithm - decryptors []CryptoAlgorithm - defaultDecryptor CryptoAlgorithm - cryptoHeaderVersion CryptoHeaderVersion -} - -func NewCrypto(encryptor CryptoAlgorithm, decryptors []CryptoAlgorithm, defaultDecryptor CryptoAlgorithm, version CryptoHeaderVersion) *Crypto { - return &Crypto{ - encryptor: encryptor, - decryptors: decryptors, - defaultDecryptor: defaultDecryptor, - cryptoHeaderVersion: version, - } -} - -func (c *Crypto) Encrypt(message []byte) ([]byte, error) { - r, e := c.encryptor.Encrypt(message) - if e != nil { - return nil, e - } - if c.encryptor.HeaderVersion() == headless { - return r.Data, nil - } - if c.cryptoHeaderVersion == CryptoHeaderV1 { - return returnWithV1Header(c.encryptor.Id(), r) - } - return nil, errors.New("unsupported crypto header version") -} - -func (c *Crypto) Decrypt(data []byte) ([]byte, error) { - cryptorId, encryptedData, err := ParseHeader(data) - if err != nil { - return nil, err - } - - if cryptorId == nil { - return c.defaultDecryptor.Decrypt(*encryptedData) - } - - return c.decryptors[0].Decrypt(*encryptedData) -} - -func (c *Crypto) EncryptStream(input io.Reader, output io.Writer) error { - _, e := c.encryptor.EncryptStream(input, output) - if e != nil { - return e - } - if c.encryptor.HeaderVersion() == headless { - return nil - } - - if c.cryptoHeaderVersion == CryptoHeaderV1 { - return nil //TODO it's too late to add header now - } - return nil //TODO -} - -func (c *Crypto) DecryptStream(input io.Reader, output io.Writer) error { - cryptorId, reader, metadata, err := ParseHeaderStream(input) - if err != nil { - return err - } - - if cryptorId == nil { - return c.defaultDecryptor.DecryptStream(reader, metadata, output) - } - - return c.decryptors[0].DecryptStream(reader, metadata, output) -} diff --git a/utils/new_crypto_test.go b/utils/new_crypto_test.go deleted file mode 100644 index de2108d6..00000000 --- a/utils/new_crypto_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package utils - -import ( - "encoding/base64" - "testing" -) - -func TestJust(t *testing.T) { - legacyCryptor, e1 := NewLegacyCryptor("enigmaenigmaenig", true) - cbcCryptor, e2 := NewCBCCryptor("enigmaenigmaenig") - - if e1 != nil || e2 != nil { - t.Errorf("error, %s, %s", e1, e2) - } - - legacy := NewCrypto(legacyCryptor, []CryptoAlgorithm{cbcCryptor}, legacyCryptor, headless) - newC := NewCrypto(cbcCryptor, []CryptoAlgorithm{cbcCryptor}, legacyCryptor, CryptoHeaderV1) - - r1, _ := legacy.Encrypt([]byte("sure i can")) - r2, _ := newC.Encrypt([]byte("sure i can")) - r3, _ := newC.Decrypt(r1) - r4, _ := newC.Decrypt(r2) - println(base64.StdEncoding.EncodeToString(r1)) - println(EncryptString("enigma", "sure i can", true)) - println(base64.StdEncoding.EncodeToString(r2)) - println(string(r3)) - println(string(r4)) -} diff --git a/utils/utils.go b/utils/utils.go deleted file mode 100644 index cd304327..00000000 --- a/utils/utils.go +++ /dev/null @@ -1,13 +0,0 @@ -package utils - -import ( - "crypto/rand" -) - -func generateIV(blocksize int) []byte { - iv := make([]byte, blocksize) - if _, err := rand.Read(iv); err != nil { - panic(err) - } - return iv -} From 35829b8abe1c68b30dfc9637b1c81a9e8ab1d082 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Thu, 14 Sep 2023 18:16:29 +0200 Subject: [PATCH 04/30] Some contract tests are working --- crypto/aes_cbc_crypto_algorithm.go | 6 +- crypto/crypto_module.go | 2 +- crypto/cryptor.go | 20 ++- crypto/cryptor_header.go | 50 ++---- crypto/cryptor_header_test.go | 1 + crypto/legacy_crypto_algorithm.go | 23 +-- crypto/legacy_crypto_algorithm_test.go | 2 +- crypto/utils.go | 4 + tests/contract/contract_test.go | 10 +- tests/contract/crypto_state_test.go | 26 +++ tests/contract/crypto_steps_test.go | 236 +++++++++++++++++++++++++ tests/contract/steps_mapping_test.go | 13 ++ tests/contract/utils_test.go | 3 + 13 files changed, 338 insertions(+), 58 deletions(-) create mode 100644 crypto/cryptor_header_test.go create mode 100644 tests/contract/crypto_state_test.go create mode 100644 tests/contract/crypto_steps_test.go diff --git a/crypto/aes_cbc_crypto_algorithm.go b/crypto/aes_cbc_crypto_algorithm.go index f3a22c17..8f6230c7 100644 --- a/crypto/aes_cbc_crypto_algorithm.go +++ b/crypto/aes_cbc_crypto_algorithm.go @@ -4,6 +4,7 @@ import ( "crypto/aes" "crypto/cipher" "crypto/sha256" + "errors" "fmt" "io" ) @@ -23,7 +24,7 @@ func NewAesCBCCryptoAlgorithm(cipherKey string) (*AesCBCCryptoAlgorithm, error) }, nil } -var crivId = "CRIV" +var crivId = "ACRH" func (c *AesCBCCryptoAlgorithm) Id() string { return crivId @@ -72,6 +73,9 @@ func (c *AesCBCCryptoAlgorithm) EncryptStream(reader io.Reader) (*EncryptedStrea } func (c *AesCBCCryptoAlgorithm) DecryptStream(encryptedData *EncryptedStreamData) (io.Reader, error) { + if encryptedData.Metadata == nil { + return nil, errors.New("missing metadata") + } return NewBlockModeDecryptingReader(encryptedData.Reader, cipher.NewCBCDecrypter(c.block, encryptedData.Metadata)), nil } diff --git a/crypto/crypto_module.go b/crypto/crypto_module.go index 3e77997e..4ed235ae 100644 --- a/crypto/crypto_module.go +++ b/crypto/crypto_module.go @@ -86,5 +86,5 @@ func (c *Module) DecryptStream(input io.Reader) (io.Reader, error) { if cryptorId == nil { return c.fallbackDecryptor.DecryptStream(data) } - return c.decryptors[*cryptorId].DecryptStream(input) + return c.decryptors[*cryptorId].DecryptStream(data) } diff --git a/crypto/cryptor.go b/crypto/cryptor.go index 19bb6746..2d1cbb65 100644 --- a/crypto/cryptor.go +++ b/crypto/cryptor.go @@ -1,7 +1,9 @@ package crypto import ( + "bufio" "bytes" + "fmt" "io" ) @@ -10,7 +12,7 @@ type Cryptor interface { Encrypt(input []byte) ([]byte, error) Decrypt(input []byte) ([]byte, error) EncryptStream(input io.Reader) (io.Reader, error) - DecryptStream(input io.Reader) (io.Reader, error) + DecryptStream(input *bufio.Reader) (io.Reader, error) } func NewCryptor(algorithm CryptoAlgorithm) Cryptor { @@ -80,11 +82,19 @@ func (c *extendedCryptorV1) EncryptStream(input io.Reader) (io.Reader, error) { return io.MultiReader(headerReader, encryptedStreamData.Reader), nil } -func (c *extendedCryptorV1) DecryptStream(input io.Reader) (io.Reader, error) { - _, readerWithOnlyData, metadata, e := parseHeaderStream(input) +func (c *extendedCryptorV1) DecryptStream(input *bufio.Reader) (io.Reader, error) { + id, readerWithOnlyData, metadata, e := parseHeaderStream(input) if e != nil { return nil, e } + + if id == nil { + return nil, fmt.Errorf("decryption error: expected id %s but got nil", c.Id()) + } + + if *id != c.Id() { + return nil, fmt.Errorf("decryption error: expected id %s but got invalid id: %s ", c.Id(), *id) + } return c.algorithm.DecryptStream(&EncryptedStreamData{Reader: readerWithOnlyData, Metadata: metadata}) } @@ -136,7 +146,7 @@ func (c *cryptorV1) EncryptStream(input io.Reader) (io.Reader, error) { return io.MultiReader(bytes.NewReader(header), bytes.NewReader(encryptedData.Data)), nil } -func (c *cryptorV1) DecryptStream(input io.Reader) (io.Reader, error) { +func (c *cryptorV1) DecryptStream(input *bufio.Reader) (io.Reader, error) { inputBytes, e := io.ReadAll(input) if e != nil { return nil, e @@ -185,6 +195,6 @@ func (c *legacyCryptor) EncryptStream(input io.Reader) (io.Reader, error) { return encryptedStreamData.Reader, nil } -func (c *legacyCryptor) DecryptStream(input io.Reader) (io.Reader, error) { +func (c *legacyCryptor) DecryptStream(input *bufio.Reader) (io.Reader, error) { return c.algorithm.DecryptStream(&EncryptedStreamData{Reader: input, Metadata: nil}) } diff --git a/crypto/cryptor_header.go b/crypto/cryptor_header.go index d585455f..2699f522 100644 --- a/crypto/cryptor_header.go +++ b/crypto/cryptor_header.go @@ -3,7 +3,7 @@ package crypto import ( "bufio" "bytes" - "errors" + "fmt" "io" "strconv" ) @@ -93,7 +93,7 @@ func peekHeaderCryptorId(data []byte) (cryptorId *string, e error) { } if data[versionPosition] != versionV1 { - return nil, errors.New("unsupported crypto header version") + return nil, unsupportedHeaderVersion(int(data[versionPosition])) } id := string(data[cryptorIdPosition : cryptorIdPosition+cryptorIdLength]) @@ -101,20 +101,15 @@ func peekHeaderCryptorId(data []byte) (cryptorId *string, e error) { } func parseHeader(data []byte) (cryptorId *string, encryptedData *EncryptedData, e error) { - if !slicesEqual(data[:len(sentinel)], sentinel[:]) { - return nil, &EncryptedData{Metadata: nil, Data: data}, nil - } - - if data[versionPosition] != versionV1 { - return nil, nil, errors.New("unsupported crypto header version") + id, err := peekHeaderCryptorId(data) + if err != nil { + return nil, nil, err } - - id := string(data[cryptorIdPosition : cryptorIdPosition+cryptorIdLength]) var headerSize int64 position := int64(sizePosition) if data[sizePosition] == longSizeIndicator { position += longSizeLength - var err error + headerSize, err = strconv.ParseInt(string(data[sizePosition:sizePosition+longSizeLength]), 10, 32) if err != nil { return nil, nil, err @@ -127,44 +122,29 @@ func parseHeader(data []byte) (cryptorId *string, encryptedData *EncryptedData, metadata := data[position : position+headerSize] position += int64(len(metadata)) - return &id, &EncryptedData{Data: data[position:], Metadata: metadata}, nil + return id, &EncryptedData{Data: data[position:], Metadata: metadata}, nil } func peekHeaderStreamCryptorId(data *bufio.Reader) (cryptorId *string, e error) { - peeked, err := data.Peek(sentinelLength + 1 + cryptorIdLength + longSizeLength) + peeked, err := data.Peek(sentinelLength + 1 + cryptorIdLength) if err != nil { - return nil, err - } - - if !slicesEqual(peeked[:len(sentinel)], sentinel[:]) { - return nil, nil - } - - if peeked[versionPosition] != versionV1 { - return nil, errors.New("unsupported crypto header version") + return nil, fmt.Errorf("decryption error: %w", err) } - id := string(peeked[cryptorIdPosition : cryptorIdPosition+cryptorIdLength]) - return &id, nil + return peekHeaderCryptorId(peeked) } -func parseHeaderStream(data io.Reader) (cryptorId []byte, d io.Reader, metadata []byte, e error) { - bufData := bufio.NewReader(data) - +func parseHeaderStream(bufData *bufio.Reader) (cryptorId *string, d io.Reader, metadata []byte, e error) { peeked, err := bufData.Peek(sentinelLength + 1 + cryptorIdLength + longSizeLength) if err != nil { - return nil, nil, nil, err - } - - if !slicesEqual(peeked[:len(sentinel)], sentinel[:]) { - return nil, bufData, nil, nil + return nil, nil, nil, fmt.Errorf("decryption error: %w", err) } - if peeked[versionPosition] != versionV1 { - return nil, nil, nil, errors.New("unsupported crypto header version") + id, err := peekHeaderCryptorId(peeked) + if err != nil { + return nil, nil, nil, err } - id := peeked[cryptorIdPosition : cryptorIdPosition+cryptorIdLength] var headerSize int64 position := int64(sizePosition) if peeked[sizePosition] == longSizeIndicator { diff --git a/crypto/cryptor_header_test.go b/crypto/cryptor_header_test.go new file mode 100644 index 00000000..5871506e --- /dev/null +++ b/crypto/cryptor_header_test.go @@ -0,0 +1 @@ +package crypto diff --git a/crypto/legacy_crypto_algorithm.go b/crypto/legacy_crypto_algorithm.go index 0f642ff0..b967159c 100644 --- a/crypto/legacy_crypto_algorithm.go +++ b/crypto/legacy_crypto_algorithm.go @@ -14,8 +14,8 @@ import ( var valIV = "0123456789012345" type LegacyCryptoAlgorithm struct { - block cipher.Block - useRandomIvForSlices bool + block cipher.Block + randomIv bool } func NewLegacyCryptoAlgorithm(cipherKey string, useRandomIV bool) (*LegacyCryptoAlgorithm, error) { @@ -25,8 +25,8 @@ func NewLegacyCryptoAlgorithm(cipherKey string, useRandomIV bool) (*LegacyCrypto } return &LegacyCryptoAlgorithm{ - block: block, - useRandomIvForSlices: useRandomIV, + block: block, + randomIv: useRandomIV, }, nil } @@ -39,7 +39,7 @@ func (c *LegacyCryptoAlgorithm) Id() string { func (c *LegacyCryptoAlgorithm) Encrypt(message []byte) (*EncryptedData, error) { message = padWithPKCS7(message) iv := make([]byte, aes.BlockSize) - if c.useRandomIvForSlices { + if c.randomIv { iv = generateIV(aes.BlockSize) } else { iv = []byte(valIV) @@ -49,7 +49,7 @@ func (c *LegacyCryptoAlgorithm) Encrypt(message []byte) (*EncryptedData, error) encryptedBytes := make([]byte, len(message)) blockmode.CryptBlocks(encryptedBytes, message) - if c.useRandomIvForSlices { + if c.randomIv { return &EncryptedData{Data: append(iv, encryptedBytes...), Metadata: nil}, nil } return &EncryptedData{Data: encryptedBytes, Metadata: nil}, nil @@ -57,11 +57,14 @@ func (c *LegacyCryptoAlgorithm) Encrypt(message []byte) (*EncryptedData, error) func (c *LegacyCryptoAlgorithm) Decrypt(encryptedData *EncryptedData) (r []byte, e error) { iv := make([]byte, aes.BlockSize) - if c.useRandomIvForSlices { - iv = generateIV(aes.BlockSize) + data := encryptedData.Data + if c.randomIv { + iv = data[:aes.BlockSize] + data = data[aes.BlockSize:] } else { iv = []byte(valIV) } + decrypter := cipher.NewCBCDecrypter(c.block, iv) //to handle decryption errors defer func() { @@ -70,8 +73,8 @@ func (c *LegacyCryptoAlgorithm) Decrypt(encryptedData *EncryptedData) (r []byte, } }() - decrypted := make([]byte, len(encryptedData.Data)) - decrypter.CryptBlocks(decrypted, encryptedData.Data) + decrypted := make([]byte, len(data)) + decrypter.CryptBlocks(decrypted, data) val, err := unpadPKCS7(decrypted) if err != nil { return nil, fmt.Errorf("decrypt error: %s", err) diff --git a/crypto/legacy_crypto_algorithm_test.go b/crypto/legacy_crypto_algorithm_test.go index 74108de6..4f7930ff 100644 --- a/crypto/legacy_crypto_algorithm_test.go +++ b/crypto/legacy_crypto_algorithm_test.go @@ -26,7 +26,7 @@ func legacyCanDecryptEncryptStreamResult(in []byte) bool { if err != nil { return false } - return bytes.Equal(in, decrypted[16:]) + return bytes.Equal(in, decrypted) } func legacyCanDecryptStreamEncryptResult(in []byte) bool { diff --git a/crypto/utils.go b/crypto/utils.go index c4b42e75..0d3a3d9c 100644 --- a/crypto/utils.go +++ b/crypto/utils.go @@ -55,3 +55,7 @@ func unpadPKCS7(data []byte) ([]byte, error) { return data[:len(data)-padlen], nil } + +func unsupportedHeaderVersion(version int) error { + return fmt.Errorf("unknown cryptor error: unsupported crypto header version %d", version) +} diff --git a/tests/contract/contract_test.go b/tests/contract/contract_test.go index af36b554..a4604bd4 100644 --- a/tests/contract/contract_test.go +++ b/tests/contract/contract_test.go @@ -13,8 +13,8 @@ var tagsFilter string var format string func TestMain(m *testing.M) { - flag.StringVar(&path, "path", "", "Path to feature files") - flag.StringVar(&tagsFilter, "tagsFilter", "~@skip && ~@na=go && ~@beta", "Tags filter") + flag.StringVar(&path, "path", "../../../sdk-specifications/features", "Path to feature files") + flag.StringVar(&tagsFilter, "tagsFilter", "~@skip && ~@na=go && @beta && @featureSet=cryptorModule && @whatever", "Tags filter") flag.StringVar(&format, "format", "pretty", "Output formatter") flag.Parse() if path == "" { @@ -28,9 +28,9 @@ func TestFeatures(t *testing.T) { suite := godog.TestSuite{ ScenarioInitializer: InitializeScenario, Options: &godog.Options{ - Format: format, - Paths: []string{path}, - Tags: tagsFilter, + Format: format, + Paths: []string{path}, + Tags: tagsFilter, TestingT: t, // Testing instance that will run subtests. }, } diff --git a/tests/contract/crypto_state_test.go b/tests/contract/crypto_state_test.go new file mode 100644 index 00000000..eb0e4963 --- /dev/null +++ b/tests/contract/crypto_state_test.go @@ -0,0 +1,26 @@ +package contract + +import ( + "context" + "io" +) + +type cryptoStateKey struct{} + +type cryptoState struct { + cryptoAlgorithm string + cipherKey string + cryptoFeaturePath string + randomIv bool + result []byte + resultReader io.Reader + err error +} + +func getCryptoState(ctx context.Context) *cryptoState { + return ctx.Value(cryptoStateKey{}).(*cryptoState) +} + +func newCryptoState(cryptoFeaturePath string) *cryptoState { + return &cryptoState{cryptoFeaturePath: cryptoFeaturePath, randomIv: false} +} diff --git a/tests/contract/crypto_steps_test.go b/tests/contract/crypto_steps_test.go new file mode 100644 index 00000000..ce15963b --- /dev/null +++ b/tests/contract/crypto_steps_test.go @@ -0,0 +1,236 @@ +package contract + +import ( + "bufio" + "bytes" + "context" + "errors" + "fmt" + "github.com/cucumber/godog" + "github.com/pubnub/go/v7/crypto" + "io" + "os" + "strings" +) + +func cryptoAlgorithm(ctx context.Context, cryptoAlgorithm string) error { + cryptoState := getCryptoState(ctx) + cryptoState.cryptoAlgorithm = cryptoAlgorithm + return nil +} + +func cryptorModuleWithRegisteredCryptoAlgorithms() error { + return godog.ErrPending +} + +func decryptedFileContentEqualToFileContent(ctx context.Context, filename string) error { + cryptoState := getCryptoState(ctx) + + file, e := os.Open(cryptoState.cryptoFeaturePath + "/assets/" + filename) + if e != nil { + return e + } + + fileContent, e := io.ReadAll(file) + if e != nil { + return e + } + + if cryptoState.result != nil { + if !bytes.Equal(cryptoState.result, fileContent) { + return errors.New("decrypted file content not equal to file content") + } + } else if cryptoState.resultReader != nil { + resultContent, e := io.ReadAll(cryptoState.resultReader) + if e != nil { + return e + } + if !bytes.Equal(resultContent, fileContent) { + return errors.New("decrypted file content not equal to file content") + } + } else { + return errors.New("no result") + } + return nil +} + +func encryptedFileSuccessfullyDecryptedByLegacyCodeWithCipherKeyAndVector(ctx context.Context, cipherKey string, iv string) error { + riv := randomIv(iv) + cryptoState := getCryptoState(ctx) + + algorithm, e := createCryptoAlgorithm("legacy", cipherKey, riv) + if e != nil { + return e + } + cryptor := crypto.NewCryptor(algorithm) + if cryptoState.result != nil { + _, err := cryptor.Decrypt(cryptoState.result) + if err != nil { + return err + } + } else if cryptoState.resultReader != nil { + _, err := cryptor.DecryptStream(bufio.NewReader(cryptoState.resultReader)) + if err != nil { + return err + } + } else { + return errors.New("no result") + } + + return nil +} + +func iDecryptFileAs(ctx context.Context, filename string, decryptionType string) error { + + cryptoState := getCryptoState(ctx) + + algorithm, e := createCryptoAlgorithm(cryptoState.cryptoAlgorithm, cryptoState.cipherKey, cryptoState.randomIv) + if e != nil { + return e + } + + cryptor := crypto.NewCryptor(algorithm) + file, e := os.Open(cryptoState.cryptoFeaturePath + "/assets/" + filename) + if e != nil { + return e + } + + if decryptionType == "stream" { + cryptoState.resultReader, e = cryptor.DecryptStream(bufio.NewReader(file)) + if e != nil { + return e + } + + } else { + fileContent, e := io.ReadAll(file) + if e != nil { + return e + } + cryptoState.result, e = cryptor.Decrypt(fileContent) + if e != nil { + return e + } + } + + return nil +} + +func iDecryptFile(ctx context.Context, filename string) error { + + cryptoState := getCryptoState(ctx) + + algorithm, e := createCryptoAlgorithm(cryptoState.cryptoAlgorithm, cryptoState.cipherKey, cryptoState.randomIv) + if e != nil { + return e + } + + cryptor := crypto.NewCryptor(algorithm) + file, e := os.Open(cryptoState.cryptoFeaturePath + "/assets/" + filename) + if e != nil { + return e + } + + _, e = cryptor.DecryptStream(bufio.NewReader(file)) + if e != nil { + cryptoState.err = e + } + _ = file.Close() + return nil +} + +func createCryptoAlgorithm(name string, cipherKey string, randomIv bool) (crypto.CryptoAlgorithm, error) { + if name == "acrh" { + return crypto.NewAesCBCCryptoAlgorithm(cipherKey) + } else if name == "legacy" { + return crypto.NewLegacyCryptoAlgorithm(cipherKey, randomIv) + } else { + return nil, fmt.Errorf("unknown crypto algorithm %s", name) + } +} + +func iEncryptFileAs(ctx context.Context, filename string, encryptionType string) error { + cryptoState := getCryptoState(ctx) + algorithm, e := createCryptoAlgorithm(cryptoState.cryptoAlgorithm, cryptoState.cipherKey, cryptoState.randomIv) + if e != nil { + return e + } + + cryptor := crypto.NewCryptor(algorithm) + file, e := os.Open(cryptoState.cryptoFeaturePath + "/assets/" + filename) + if e != nil { + return e + } + if encryptionType == "stream" { + cryptoState.resultReader, e = cryptor.EncryptStream(bufio.NewReader(file)) + if e != nil { + return e + } + } else { + content, e := io.ReadAll(file) + if e != nil { + return e + } + cryptoState.result, e = cryptor.Encrypt(content) + if e != nil { + return e + } + + } + return nil +} + +func iReceiveDecryptionError(ctx context.Context) error { + cryptoState := getCryptoState(ctx) + if cryptoState.err != nil { + if strings.HasPrefix(cryptoState.err.Error(), "decryption error") { + return nil + } else { + return cryptoState.err + } + } else { + return errors.New("expected error") + } +} + +func iReceiveSuccess(ctx context.Context) error { + cryptoState := getCryptoState(ctx) + if cryptoState.err != nil { + return cryptoState.err + } + return nil +} + +func iReceiveUnknownCryptorError(ctx context.Context) error { + cryptoState := getCryptoState(ctx) + if cryptoState.err != nil { + if strings.Contains(cryptoState.err.Error(), "unknown cryptor error") { + return nil + } else { + return cryptoState.err + } + } else { + return errors.New("expected error") + } +} + +func withCipherKey(ctx context.Context, cipherKey string) error { + cryptoState := getCryptoState(ctx) + cryptoState.cipherKey = cipherKey + return nil +} + +func randomIv(iv string) bool { + if iv == "constant" { + return false + } else if iv == "random" { + return true + } else { + return false + } +} + +func withVector(ctx context.Context, iv string) error { + cryptoState := getCryptoState(ctx) + cryptoState.randomIv = randomIv(iv) + return nil +} diff --git a/tests/contract/steps_mapping_test.go b/tests/contract/steps_mapping_test.go index 4e41059c..1b4de34e 100644 --- a/tests/contract/steps_mapping_test.go +++ b/tests/contract/steps_mapping_test.go @@ -55,4 +55,17 @@ func MapSteps(ctx *godog.ScenarioContext) { ctx.Step(`^the error detail message is not empty$`, theErrorDetailMessageIsNotEmpty) ctx.Step(`^the result is successful$`, theResultIsSuccessful) ctx.Step(`^the token string \'(.*)\'$`, theTokenString) + + ctx.Step(`^\'(.*)\' crypto algorithm$`, cryptoAlgorithm) + ctx.Step(`^cryptor module with registered \'(.*)\' and \'(.*)\' crypto algorithms$`, cryptorModuleWithRegisteredCryptoAlgorithms) + ctx.Step(`^Decrypted file content equal to the \'(.*)\' file content$`, decryptedFileContentEqualToFileContent) + ctx.Step(`^Encrypted file successfully decrypted by legacy code with \'(.*)\' cipher key and \'(.*)\' vector$`, encryptedFileSuccessfullyDecryptedByLegacyCodeWithCipherKeyAndVector) + ctx.Step(`^I decrypt \'(.*)\' file as \'(.*)\'$`, iDecryptFileAs) + ctx.Step(`^I decrypt \'(.*)\' file$`, iDecryptFile) + ctx.Step(`^I encrypt \'(.*)\' file as \'(.*)\'$`, iEncryptFileAs) + ctx.Step(`^I receive \'decryption error\'$`, iReceiveDecryptionError) + ctx.Step(`^I receive \'success\'$`, iReceiveSuccess) + ctx.Step(`^I receive \'unknown cryptor error\'$`, iReceiveUnknownCryptorError) + ctx.Step(`^with \'(.*)\' cipher key$`, withCipherKey) + ctx.Step(`^with \'(.*)\' vector$`, withVector) } diff --git a/tests/contract/utils_test.go b/tests/contract/utils_test.go index 6a212dfb..b26c8ba7 100644 --- a/tests/contract/utils_test.go +++ b/tests/contract/utils_test.go @@ -46,6 +46,7 @@ func before(ctx context.Context, sc *godog.Scenario) (context.Context, error) { newCtx = context.WithValue(newCtx, commonStateKey{}, commonState) newCtx = context.WithValue(newCtx, accessStateKey{}, accessState) + newCtx = context.WithValue(newCtx, cryptoStateKey{}, newCryptoState(contractTestConfig.featurePath+"/encryption")) if !contractTestConfig.serverMock { return newCtx, nil @@ -116,6 +117,7 @@ type contractTestConfig struct { serverMock bool hostPort string secure bool + featurePath string } func newContractTestConfig() (contractTestConfig, error) { @@ -135,6 +137,7 @@ func newContractTestConfig() (contractTestConfig, error) { hostPort: getenvWithDefault("HOST_PORT", "localhost:8090"), serverMock: serverMock, secure: secure, + featurePath: path, }, err } From 37a1ef22bf0b1219b6e6c96075e0b714223d9d2c Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Fri, 15 Sep 2023 14:40:59 +0200 Subject: [PATCH 05/30] Reduce number of layers --- ...crypto_algorithm.go => aes_cbc_cryptor.go} | 16 +- ...orithm_test.go => aes_cbc_cryptor_test.go} | 8 +- crypto/crypto_algorithm.go | 25 --- crypto/crypto_module.go | 90 -------- crypto/crypto_module_test.go | 28 --- crypto/cryptor.go | 205 ++---------------- crypto/cryptor_header.go | 59 +++-- crypto/cryptor_header_test.go | 1 - crypto/decrypting_reader.go | 12 +- crypto/default_extended_cryptor.go | 55 +++++ crypto/encrypting_reader.go | 10 +- crypto/encrypting_reader_test.go | 5 - ..._crypto_algorithm.go => legacy_cryptor.go} | 18 +- ...gorithm_test.go => legacy_cryptor_test.go} | 8 +- crypto/module.go | 131 +++++++++++ crypto/utils.go | 2 +- tests/contract/contract_test.go | 2 +- tests/contract/crypto_state_test.go | 21 +- tests/contract/crypto_steps_test.go | 59 +++-- tests/contract/steps_mapping_test.go | 4 +- utils/crypto.go | 6 +- 21 files changed, 320 insertions(+), 445 deletions(-) rename crypto/{aes_cbc_crypto_algorithm.go => aes_cbc_cryptor.go} (73%) rename crypto/{aes_cbc_crypto_algorithm_test.go => aes_cbc_cryptor_test.go} (84%) delete mode 100644 crypto/crypto_algorithm.go delete mode 100644 crypto/crypto_module.go delete mode 100644 crypto/crypto_module_test.go delete mode 100644 crypto/cryptor_header_test.go create mode 100644 crypto/default_extended_cryptor.go rename crypto/{legacy_crypto_algorithm.go => legacy_cryptor.go} (81%) rename crypto/{legacy_crypto_algorithm_test.go => legacy_cryptor_test.go} (83%) create mode 100644 crypto/module.go diff --git a/crypto/aes_cbc_crypto_algorithm.go b/crypto/aes_cbc_cryptor.go similarity index 73% rename from crypto/aes_cbc_crypto_algorithm.go rename to crypto/aes_cbc_cryptor.go index 8f6230c7..229e04d2 100644 --- a/crypto/aes_cbc_crypto_algorithm.go +++ b/crypto/aes_cbc_cryptor.go @@ -9,28 +9,28 @@ import ( "io" ) -type AesCBCCryptoAlgorithm struct { +type aesCbcCryptor struct { block cipher.Block } -func NewAesCBCCryptoAlgorithm(cipherKey string) (*AesCBCCryptoAlgorithm, error) { +func NewAesCbcCryptor(cipherKey string) (ExtendedCryptor, error) { block, e := aesCipher(cipherKey) if e != nil { return nil, e } - return &AesCBCCryptoAlgorithm{ + return &aesCbcCryptor{ block: block, }, nil } var crivId = "ACRH" -func (c *AesCBCCryptoAlgorithm) Id() string { +func (c *aesCbcCryptor) Id() string { return crivId } -func (c *AesCBCCryptoAlgorithm) Encrypt(message []byte) (*EncryptedData, error) { +func (c *aesCbcCryptor) Encrypt(message []byte) (*EncryptedData, error) { message = padWithPKCS7(message) iv := generateIV(aes.BlockSize) blockmode := cipher.NewCBCEncrypter(c.block, iv) @@ -44,7 +44,7 @@ func (c *AesCBCCryptoAlgorithm) Encrypt(message []byte) (*EncryptedData, error) }, nil } -func (c *AesCBCCryptoAlgorithm) Decrypt(encryptedData *EncryptedData) (r []byte, e error) { +func (c *aesCbcCryptor) Decrypt(encryptedData *EncryptedData) (r []byte, e error) { decrypter := cipher.NewCBCDecrypter(c.block, encryptedData.Metadata) //to handle decryption errors defer func() { @@ -63,7 +63,7 @@ func (c *AesCBCCryptoAlgorithm) Decrypt(encryptedData *EncryptedData) (r []byte, return val, nil } -func (c *AesCBCCryptoAlgorithm) EncryptStream(reader io.Reader) (*EncryptedStreamData, error) { +func (c *aesCbcCryptor) EncryptStream(reader io.Reader) (*EncryptedStreamData, error) { iv := generateIV(aes.BlockSize) return &EncryptedStreamData{ @@ -72,7 +72,7 @@ func (c *AesCBCCryptoAlgorithm) EncryptStream(reader io.Reader) (*EncryptedStrea }, nil } -func (c *AesCBCCryptoAlgorithm) DecryptStream(encryptedData *EncryptedStreamData) (io.Reader, error) { +func (c *aesCbcCryptor) DecryptStream(encryptedData *EncryptedStreamData) (io.Reader, error) { if encryptedData.Metadata == nil { return nil, errors.New("missing metadata") } diff --git a/crypto/aes_cbc_crypto_algorithm_test.go b/crypto/aes_cbc_cryptor_test.go similarity index 84% rename from crypto/aes_cbc_crypto_algorithm_test.go rename to crypto/aes_cbc_cryptor_test.go index d022d8ea..108ce130 100644 --- a/crypto/aes_cbc_crypto_algorithm_test.go +++ b/crypto/aes_cbc_cryptor_test.go @@ -8,19 +8,19 @@ import ( ) func canDecryptEncryptStreamResult(in []byte) bool { - cryptoAlgorithm, e := NewAesCBCCryptoAlgorithm("enigma") + cryptor, e := NewAesCbcCryptor("enigma") if e != nil { return false } - output, err := cryptoAlgorithm.EncryptStream(bytes.NewReader(in)) + output, err := cryptor.EncryptStream(bytes.NewReader(in)) if err != nil { return false } encryptedData, err := io.ReadAll(output.Reader) - decrypted, err := cryptoAlgorithm.Decrypt(&EncryptedData{ + decrypted, err := cryptor.Decrypt(&EncryptedData{ Data: encryptedData, Metadata: output.Metadata, }) @@ -31,7 +31,7 @@ func canDecryptEncryptStreamResult(in []byte) bool { } func canDecryptStreamEncryptResult(in []byte) bool { - cryptor, e := NewAesCBCCryptoAlgorithm("enigma") + cryptor, e := NewAesCbcCryptor("enigma") if e != nil { return false } diff --git a/crypto/crypto_algorithm.go b/crypto/crypto_algorithm.go deleted file mode 100644 index 945cdf24..00000000 --- a/crypto/crypto_algorithm.go +++ /dev/null @@ -1,25 +0,0 @@ -package crypto - -import "io" - -type EncryptedData struct { - Metadata []byte - Data []byte -} - -type CryptoAlgorithm interface { - Id() string - Encrypt(message []byte) (*EncryptedData, error) - Decrypt(encryptedData *EncryptedData) ([]byte, error) -} - -type EncryptedStreamData struct { - Metadata []byte - Reader io.Reader -} - -type ExtendedCryptoAlgorithm interface { - CryptoAlgorithm - EncryptStream(reader io.Reader) (*EncryptedStreamData, error) - DecryptStream(encryptedData *EncryptedStreamData) (io.Reader, error) -} diff --git a/crypto/crypto_module.go b/crypto/crypto_module.go deleted file mode 100644 index 4ed235ae..00000000 --- a/crypto/crypto_module.go +++ /dev/null @@ -1,90 +0,0 @@ -package crypto - -import ( - "bufio" - "io" -) - -type Module struct { - encryptor Cryptor - decryptors map[string]Cryptor - fallbackDecryptor Cryptor -} - -func NewLegacyCryptoModule(cipherKey string, randomIv bool) (*Module, error) { - legacyCryptoAlgorithm, e := NewLegacyCryptoAlgorithm(cipherKey, randomIv) - if e != nil { - return nil, e - } - aesCBCCryptoAlgorithm, e := NewAesCBCCryptoAlgorithm(cipherKey) - - if e != nil { - return nil, e - } - - decryptors := []CryptoAlgorithm{aesCBCCryptoAlgorithm} - return newCryptoModule(legacyCryptoAlgorithm, decryptors, legacyCryptoAlgorithm), nil -} - -func NewCryptoModule(cipherKey string, randomIv bool) (*Module, error) { - aesCBCCryptoAlgorithm, e := NewAesCBCCryptoAlgorithm(cipherKey) - if e != nil { - return nil, e - } - - legacyCryptoAlgorithm, e := NewLegacyCryptoAlgorithm(cipherKey, randomIv) - if e != nil { - return nil, e - } - - decryptors := []CryptoAlgorithm{aesCBCCryptoAlgorithm} - return newCryptoModule(aesCBCCryptoAlgorithm, decryptors, legacyCryptoAlgorithm), nil -} - -func newCryptoModule(encryptingAlgorithm CryptoAlgorithm, decryptingAlgorithms []CryptoAlgorithm, fallbackDecryptingAlgorithm CryptoAlgorithm) *Module { - - decryptors := make(map[string]Cryptor, len(decryptingAlgorithms)) - for _, decryptor := range decryptingAlgorithms { - decryptors[decryptor.Id()] = NewCryptor(decryptor) - } - - encryptor := NewCryptor(encryptingAlgorithm) - fallbackDecryptor := NewCryptor(fallbackDecryptingAlgorithm) - - return &Module{ - encryptor: encryptor, - decryptors: decryptors, - fallbackDecryptor: fallbackDecryptor, - } -} - -func (c *Module) Encrypt(message []byte) ([]byte, error) { - return c.encryptor.Encrypt(message) -} - -func (c *Module) Decrypt(data []byte) ([]byte, error) { - cryptorId, e := peekHeaderCryptorId(data) - if e != nil { - return nil, e - } - if cryptorId == nil { - return c.fallbackDecryptor.Decrypt(data) - } - return c.decryptors[*cryptorId].Decrypt(data) -} - -func (c *Module) EncryptStream(input io.Reader) (io.Reader, error) { - return c.encryptor.EncryptStream(input) -} - -func (c *Module) DecryptStream(input io.Reader) (io.Reader, error) { - data := bufio.NewReader(input) - cryptorId, e := peekHeaderStreamCryptorId(data) - if e != nil { - return nil, e - } - if cryptorId == nil { - return c.fallbackDecryptor.DecryptStream(data) - } - return c.decryptors[*cryptorId].DecryptStream(data) -} diff --git a/crypto/crypto_module_test.go b/crypto/crypto_module_test.go deleted file mode 100644 index 65e861d3..00000000 --- a/crypto/crypto_module_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package crypto - -import ( - "encoding/base64" - "testing" -) - -func TestJust(t *testing.T) { - cipherKey := "enigma" - - legacy, e := NewLegacyCryptoModule(cipherKey, true) - if e != nil { - t.Errorf(e.Error()) - } - newC, e := NewCryptoModule(cipherKey, true) - if e != nil { - t.Errorf(e.Error()) - } - - r1, _ := legacy.Encrypt([]byte("sure i can")) - r2, _ := newC.Encrypt([]byte("sure i can")) - r3, _ := newC.Decrypt(r1) - r4, _ := newC.Decrypt(r2) - println(base64.StdEncoding.EncodeToString(r1)) - println(base64.StdEncoding.EncodeToString(r2)) - println(string(r3)) - println(string(r4)) -} diff --git a/crypto/cryptor.go b/crypto/cryptor.go index 2d1cbb65..3eb1c434 100644 --- a/crypto/cryptor.go +++ b/crypto/cryptor.go @@ -1,200 +1,25 @@ package crypto -import ( - "bufio" - "bytes" - "fmt" - "io" -) +import "io" -type Cryptor interface { - Id() string - Encrypt(input []byte) ([]byte, error) - Decrypt(input []byte) ([]byte, error) - EncryptStream(input io.Reader) (io.Reader, error) - DecryptStream(input *bufio.Reader) (io.Reader, error) -} - -func NewCryptor(algorithm CryptoAlgorithm) Cryptor { - var c Cryptor - if legacy, ok := algorithm.(*LegacyCryptoAlgorithm); ok { - c = newLegacyCryptor(legacy) - } else if extended, ok := algorithm.(ExtendedCryptoAlgorithm); ok { - c = newExtendedCryptorV1(extended) - } else { - c = newCryptorV1(algorithm.(CryptoAlgorithm)) - } - return c -} - -func newExtendedCryptorV1(algorithm ExtendedCryptoAlgorithm) Cryptor { - var c Cryptor - c = &extendedCryptorV1{ - cryptorV1: cryptorV1{ - algorithm: algorithm, - }, - algorithm: algorithm, - } - return c -} - -func newCryptorV1(algorithm CryptoAlgorithm) Cryptor { - var c Cryptor - c = &cryptorV1{ - algorithm: algorithm, - } - return c -} - -func newLegacyCryptor(algorithm ExtendedCryptoAlgorithm) Cryptor { - var c Cryptor - c = &legacyCryptor{ - algorithm: algorithm, - } - return c -} - -type extendedCryptorV1 struct { - cryptorV1 - algorithm ExtendedCryptoAlgorithm -} - -//func (c *extendedCryptorV1) Encrypt(message []byte) ([]byte, error) { -// return c.cryptorV1.Encrypt(message) -//} -// -//func (c *extendedCryptorV1) Decrypt(message []byte) ([]byte, error) { -// return c.cryptorV1.Decrypt(message) -//} - -func (c *extendedCryptorV1) EncryptStream(input io.Reader) (io.Reader, error) { - encryptedStreamData, e := c.algorithm.EncryptStream(input) - if e != nil { - return nil, e - } - header, e := headerV1(c.algorithm.Id(), encryptedStreamData.Metadata) - if e != nil { - return nil, e - } - - headerReader := bytes.NewReader(header) - - return io.MultiReader(headerReader, encryptedStreamData.Reader), nil -} - -func (c *extendedCryptorV1) DecryptStream(input *bufio.Reader) (io.Reader, error) { - id, readerWithOnlyData, metadata, e := parseHeaderStream(input) - if e != nil { - return nil, e - } - - if id == nil { - return nil, fmt.Errorf("decryption error: expected id %s but got nil", c.Id()) - } - - if *id != c.Id() { - return nil, fmt.Errorf("decryption error: expected id %s but got invalid id: %s ", c.Id(), *id) - } - return c.algorithm.DecryptStream(&EncryptedStreamData{Reader: readerWithOnlyData, Metadata: metadata}) -} - -type cryptorV1 struct { - algorithm CryptoAlgorithm -} - -func (c *cryptorV1) Id() string { - return c.algorithm.Id() +type EncryptedData struct { + Metadata []byte + Data []byte } -func (c *cryptorV1) Encrypt(message []byte) ([]byte, error) { - encryptedData, e := c.algorithm.Encrypt(message) - if e != nil { - return nil, e - } - header, e := headerV1(c.algorithm.Id(), encryptedData.Metadata) - if e != nil { - return nil, e - } - - return append(header, encryptedData.Data...), nil -} - -func (c *cryptorV1) Decrypt(message []byte) ([]byte, error) { - _, encryptedData, e := parseHeader(message) - if e != nil { - return nil, e - } - return c.algorithm.Decrypt(encryptedData) -} - -func (c *cryptorV1) EncryptStream(input io.Reader) (io.Reader, error) { - inputBytes, e := io.ReadAll(input) - if e != nil { - return nil, e - } - - encryptedData, e := c.algorithm.Encrypt(inputBytes) - if e != nil { - return nil, e - } - - header, e := headerV1(c.algorithm.Id(), encryptedData.Metadata) - if e != nil { - return nil, e - } - - return io.MultiReader(bytes.NewReader(header), bytes.NewReader(encryptedData.Data)), nil -} - -func (c *cryptorV1) DecryptStream(input *bufio.Reader) (io.Reader, error) { - inputBytes, e := io.ReadAll(input) - if e != nil { - return nil, e - } - - _, encryptedData, e := parseHeader(inputBytes) - if e != nil { - return nil, e - } - - decryptedData, e := c.algorithm.Decrypt(encryptedData) - if e != nil { - return nil, e - } - - return bytes.NewReader(decryptedData), nil -} - -type legacyCryptor struct { - algorithm ExtendedCryptoAlgorithm -} - -func (c *legacyCryptor) Id() string { - return c.algorithm.Id() -} - -func (c *legacyCryptor) Encrypt(message []byte) ([]byte, error) { - encryptedData, e := c.algorithm.Encrypt(message) - if e != nil { - return nil, e - } - - return encryptedData.Data, nil -} - -func (c *legacyCryptor) Decrypt(message []byte) ([]byte, error) { - return c.algorithm.Decrypt(&EncryptedData{Data: message, Metadata: nil}) +type Cryptor interface { + Id() string + Encrypt(message []byte) (*EncryptedData, error) + Decrypt(encryptedData *EncryptedData) ([]byte, error) } -func (c *legacyCryptor) EncryptStream(input io.Reader) (io.Reader, error) { - encryptedStreamData, e := c.algorithm.EncryptStream(input) - if e != nil { - return nil, e - } - - return encryptedStreamData.Reader, nil +type EncryptedStreamData struct { + Metadata []byte + Reader io.Reader } -func (c *legacyCryptor) DecryptStream(input *bufio.Reader) (io.Reader, error) { - return c.algorithm.DecryptStream(&EncryptedStreamData{Reader: input, Metadata: nil}) +type ExtendedCryptor interface { + Cryptor + EncryptStream(reader io.Reader) (*EncryptedStreamData, error) + DecryptStream(encryptedData *EncryptedStreamData) (io.Reader, error) } diff --git a/crypto/cryptor_header.go b/crypto/cryptor_header.go index 2699f522..454833cc 100644 --- a/crypto/cryptor_header.go +++ b/crypto/cryptor_header.go @@ -8,13 +8,6 @@ import ( "strconv" ) -type CryptoHeaderVersion int - -const ( - headless CryptoHeaderVersion = iota - CryptoHeaderV1 -) - const versionPosition = 4 const versionV1 = 1 const cryptorIdPosition = 5 @@ -89,7 +82,7 @@ func slicesEqual(a, b []byte) bool { func peekHeaderCryptorId(data []byte) (cryptorId *string, e error) { if !slicesEqual(data[:len(sentinel)], sentinel[:]) { - return nil, nil + return &legacyId, nil } if data[versionPosition] != versionV1 { @@ -105,6 +98,9 @@ func parseHeader(data []byte) (cryptorId *string, encryptedData *EncryptedData, if err != nil { return nil, nil, err } + if (*id) == legacyId { + return id, &EncryptedData{Data: data, Metadata: nil}, nil + } var headerSize int64 position := int64(sizePosition) if data[sizePosition] == longSizeIndicator { @@ -125,54 +121,55 @@ func parseHeader(data []byte) (cryptorId *string, encryptedData *EncryptedData, return id, &EncryptedData{Data: data[position:], Metadata: metadata}, nil } -func peekHeaderStreamCryptorId(data *bufio.Reader) (cryptorId *string, e error) { - peeked, err := data.Peek(sentinelLength + 1 + cryptorIdLength) - if err != nil { - return nil, fmt.Errorf("decryption error: %w", err) - } - - return peekHeaderCryptorId(peeked) -} - -func parseHeaderStream(bufData *bufio.Reader) (cryptorId *string, d io.Reader, metadata []byte, e error) { +func parseHeaderStream(bufData *bufio.Reader) (cryptorId *string, encrypted *EncryptedStreamData, e error) { peeked, err := bufData.Peek(sentinelLength + 1 + cryptorIdLength + longSizeLength) if err != nil { - return nil, nil, nil, fmt.Errorf("decryption error: %w", err) + return nil, nil, fmt.Errorf("decryption error: %w", err) } id, err := peekHeaderCryptorId(peeked) if err != nil { - return nil, nil, nil, err + return nil, nil, err } - var headerSize int64 + if (*id) == legacyId { + return id, &EncryptedStreamData{ + Reader: bufData, + Metadata: nil, + }, nil + } + + var metadataSize int64 position := int64(sizePosition) if peeked[sizePosition] == longSizeIndicator { position += longSizeLength var e error - headerSize, e = strconv.ParseInt(string(peeked[sizePosition:sizePosition+longSizeLength]), 10, 32) + metadataSize, e = strconv.ParseInt(string(peeked[sizePosition:sizePosition+longSizeLength]), 10, 32) if e != nil { - return nil, nil, nil, e + return nil, nil, e } } else { position += shortSizeLength - headerSize = int64(peeked[sizePosition]) + metadataSize = int64(peeked[sizePosition]) } - if headerSize > 254 { + if metadataSize > 254 { _, e := bufData.Discard(sentinelLength + 1 + cryptorIdLength + longSizeLength) if e != nil { - return nil, nil, nil, e + return nil, nil, e } } else { _, e := bufData.Discard(sentinelLength + 1 + cryptorIdLength + shortSizeLength) if e != nil { - return nil, nil, nil, e + return nil, nil, e } } - m := make([]byte, headerSize) - _, e = io.ReadFull(bufData, metadata) + m := make([]byte, metadataSize) + _, e = io.ReadFull(bufData, m) if e != nil { - return nil, nil, nil, e + return nil, nil, e } - return id, bufData, m, nil + return id, &EncryptedStreamData{ + Reader: bufData, + Metadata: m, + }, nil } diff --git a/crypto/cryptor_header_test.go b/crypto/cryptor_header_test.go deleted file mode 100644 index 5871506e..00000000 --- a/crypto/cryptor_header_test.go +++ /dev/null @@ -1 +0,0 @@ -package crypto diff --git a/crypto/decrypting_reader.go b/crypto/decrypting_reader.go index 41d9faa4..2ebe412d 100644 --- a/crypto/decrypting_reader.go +++ b/crypto/decrypting_reader.go @@ -9,7 +9,7 @@ import ( ) func NewBlockModeDecryptingReader(r io.Reader, mode cipher.BlockMode) io.Reader { - return &BlockModeDecryptingReader{ + return &blockModeDecryptingReader{ r: bufio.NewReader(r), blockMode: mode, buffer: bytes.NewBuffer(nil), @@ -17,14 +17,14 @@ func NewBlockModeDecryptingReader(r io.Reader, mode cipher.BlockMode) io.Reader } } -type BlockModeDecryptingReader struct { +type blockModeDecryptingReader struct { r *bufio.Reader blockMode cipher.BlockMode buffer *bytes.Buffer err error } -func (decryptingReader *BlockModeDecryptingReader) readNextBlock() ([]byte, error) { +func (decryptingReader *blockModeDecryptingReader) readNextBlock() ([]byte, error) { reader := decryptingReader.r output := make([]byte, decryptingReader.blockMode.BlockSize()) @@ -44,7 +44,7 @@ func (decryptingReader *BlockModeDecryptingReader) readNextBlock() ([]byte, erro return output, nil } -func (decryptingReader *BlockModeDecryptingReader) decryptUntilPFull(p []byte) (n int, err error) { +func (decryptingReader *blockModeDecryptingReader) decryptUntilPFull(p []byte) (n int, err error) { var copied int var block []byte var e error @@ -100,7 +100,7 @@ func readFromBufferUntilEmpty(buffer *bytes.Buffer, p []byte) int { return copy(p, buffered) } -func (decryptingReader *BlockModeDecryptingReader) readFromBufferUntilEmpty(p []byte) (n int, err error) { +func (decryptingReader *blockModeDecryptingReader) readFromBufferUntilEmpty(p []byte) (n int, err error) { buffered := decryptingReader.buffer.Next(len(p)) var alreadyWrote int if len(buffered) > 0 { @@ -114,7 +114,7 @@ func (decryptingReader *BlockModeDecryptingReader) readFromBufferUntilEmpty(p [] return alreadyWrote, nil } -func (decryptingReader *BlockModeDecryptingReader) Read(p []byte) (n int, err error) { +func (decryptingReader *blockModeDecryptingReader) Read(p []byte) (n int, err error) { if len(p) == 0 { return 0, errors.New("cannot read into empty buffer") } diff --git a/crypto/default_extended_cryptor.go b/crypto/default_extended_cryptor.go new file mode 100644 index 00000000..290d3454 --- /dev/null +++ b/crypto/default_extended_cryptor.go @@ -0,0 +1,55 @@ +package crypto + +import ( + "bytes" + "io" +) + +type defaultExtendedCryptor struct { + Cryptor +} + +func liftToExtendedCryptor(cryptor Cryptor) ExtendedCryptor { + if extendedCryptor, ok := cryptor.(ExtendedCryptor); ok { + return extendedCryptor + } else { + return newDefaultExtendedCryptor(cryptor) + } +} + +func newDefaultExtendedCryptor(cryptor Cryptor) ExtendedCryptor { + return &defaultExtendedCryptor{ + cryptor, + } +} + +func (c *defaultExtendedCryptor) EncryptStream(input io.Reader) (*EncryptedStreamData, error) { + inputBytes, e := io.ReadAll(input) + if e != nil { + return nil, e + } + + encryptedData, e := c.Cryptor.Encrypt(inputBytes) + if e != nil { + return nil, e + } + + return &EncryptedStreamData{ + Reader: bytes.NewReader(encryptedData.Data), + Metadata: encryptedData.Metadata, + }, nil +} + +func (c *defaultExtendedCryptor) DecryptStream(input *EncryptedStreamData) (io.Reader, error) { + inputBytes, e := io.ReadAll(input.Reader) + if e != nil { + return nil, e + } + + decryptedData, e := c.Cryptor.Decrypt(&EncryptedData{Data: inputBytes, Metadata: input.Metadata}) + if e != nil { + return nil, e + } + + return bytes.NewReader(decryptedData), nil +} diff --git a/crypto/encrypting_reader.go b/crypto/encrypting_reader.go index 523121b8..d807530f 100644 --- a/crypto/encrypting_reader.go +++ b/crypto/encrypting_reader.go @@ -9,7 +9,7 @@ import ( ) func NewBlockModeEncryptingReader(r io.Reader, mode cipher.BlockMode) io.Reader { - return &BlockModeEncryptingReader{ + return &blockModeEncryptingReader{ r: bufio.NewReader(r), blockMode: mode, buffer: bytes.NewBuffer(nil), @@ -17,14 +17,14 @@ func NewBlockModeEncryptingReader(r io.Reader, mode cipher.BlockMode) io.Reader } } -type BlockModeEncryptingReader struct { +type blockModeEncryptingReader struct { r *bufio.Reader blockMode cipher.BlockMode buffer *bytes.Buffer err error } -func (encryptingReader *BlockModeEncryptingReader) readNextBlockPadded() (read []byte, err error) { +func (encryptingReader *blockModeEncryptingReader) readNextBlockPadded() (read []byte, err error) { output := make([]byte, encryptingReader.blockMode.BlockSize()) sizeOfCurrentlyRead, readErr := io.ReadFull(encryptingReader.r, output) if readErr != nil && readErr != io.EOF && !errors.Is(readErr, io.ErrUnexpectedEOF) { @@ -42,7 +42,7 @@ func (encryptingReader *BlockModeEncryptingReader) readNextBlockPadded() (read [ return output, nil } -func (encryptingReader *BlockModeEncryptingReader) encryptUntilPFull(p []byte) (int, error) { +func (encryptingReader *blockModeEncryptingReader) encryptUntilPFull(p []byte) (int, error) { var alreadyWrote int for alreadyWrote <= len(p) { block, e := encryptingReader.readNextBlockPadded() @@ -65,7 +65,7 @@ func (encryptingReader *BlockModeEncryptingReader) encryptUntilPFull(p []byte) ( return alreadyWrote, nil } -func (encryptingReader *BlockModeEncryptingReader) Read(p []byte) (n int, err error) { +func (encryptingReader *blockModeEncryptingReader) Read(p []byte) (n int, err error) { if len(p) == 0 { return 0, errors.New("cannot read into empty buffer") } diff --git a/crypto/encrypting_reader_test.go b/crypto/encrypting_reader_test.go index 56cfcb6e..dc711dce 100644 --- a/crypto/encrypting_reader_test.go +++ b/crypto/encrypting_reader_test.go @@ -2,7 +2,6 @@ package crypto import ( "bytes" - "github.com/stretchr/testify/assert" "testing" "testing/quick" ) @@ -37,7 +36,3 @@ func Test_EncryptingReader_ReadDifferentSizeOfBuffers(t *testing.T) { t.Error(err) } } - -func Test_EncryptingReader_ReadDifferentSizeOfBuffers1(t *testing.T) { - assert.True(t, encryptingReaderCanReadDifferentSizeOfChunks([]byte{0x25, 0xb4, 0x40, 0x82, 0x6c, 0xbf, 0x30, 0xd1, 0xad, 0x8e, 0x31, 0x4f, 0x72, 0xd5, 0xbb, 0xd3, 0xc7, 0x2c, 0xf1, 0x60, 0x68, 0x76, 0x98, 0xda, 0x36, 0x3d, 0xe5, 0xc0, 0xfd, 0xd1, 0x57, 0x44, 0xca, 0xcf, 0xd3, 0xd1}, 0x21)) -} diff --git a/crypto/legacy_crypto_algorithm.go b/crypto/legacy_cryptor.go similarity index 81% rename from crypto/legacy_crypto_algorithm.go rename to crypto/legacy_cryptor.go index b967159c..5e51758b 100644 --- a/crypto/legacy_crypto_algorithm.go +++ b/crypto/legacy_cryptor.go @@ -13,30 +13,30 @@ import ( // 16 byte constant legacy IV var valIV = "0123456789012345" -type LegacyCryptoAlgorithm struct { +type legacyCryptor struct { block cipher.Block randomIv bool } -func NewLegacyCryptoAlgorithm(cipherKey string, useRandomIV bool) (*LegacyCryptoAlgorithm, error) { +func NewLegacyCryptor(cipherKey string, useRandomIV bool) (ExtendedCryptor, error) { block, e := legacyAesCipher(cipherKey) if e != nil { return nil, e } - return &LegacyCryptoAlgorithm{ + return &legacyCryptor{ block: block, randomIv: useRandomIV, }, nil } -var legacyId = "1234" +var legacyId = string([]byte{0x00, 0x00, 0x00, 0x00}) -func (c *LegacyCryptoAlgorithm) Id() string { +func (c *legacyCryptor) Id() string { return legacyId } -func (c *LegacyCryptoAlgorithm) Encrypt(message []byte) (*EncryptedData, error) { +func (c *legacyCryptor) Encrypt(message []byte) (*EncryptedData, error) { message = padWithPKCS7(message) iv := make([]byte, aes.BlockSize) if c.randomIv { @@ -55,7 +55,7 @@ func (c *LegacyCryptoAlgorithm) Encrypt(message []byte) (*EncryptedData, error) return &EncryptedData{Data: encryptedBytes, Metadata: nil}, nil } -func (c *LegacyCryptoAlgorithm) Decrypt(encryptedData *EncryptedData) (r []byte, e error) { +func (c *legacyCryptor) Decrypt(encryptedData *EncryptedData) (r []byte, e error) { iv := make([]byte, aes.BlockSize) data := encryptedData.Data if c.randomIv { @@ -83,7 +83,7 @@ func (c *LegacyCryptoAlgorithm) Decrypt(encryptedData *EncryptedData) (r []byte, return val, nil } -func (c *LegacyCryptoAlgorithm) EncryptStream(reader io.Reader) (*EncryptedStreamData, error) { +func (c *legacyCryptor) EncryptStream(reader io.Reader) (*EncryptedStreamData, error) { iv := generateIV(aes.BlockSize) return &EncryptedStreamData{ @@ -92,7 +92,7 @@ func (c *LegacyCryptoAlgorithm) EncryptStream(reader io.Reader) (*EncryptedStrea }, nil } -func (c *LegacyCryptoAlgorithm) DecryptStream(encryptedData *EncryptedStreamData) (io.Reader, error) { +func (c *legacyCryptor) DecryptStream(encryptedData *EncryptedStreamData) (io.Reader, error) { iv := make([]byte, aes.BlockSize) _, err := io.ReadFull(encryptedData.Reader, iv) if err != nil { diff --git a/crypto/legacy_crypto_algorithm_test.go b/crypto/legacy_cryptor_test.go similarity index 83% rename from crypto/legacy_crypto_algorithm_test.go rename to crypto/legacy_cryptor_test.go index 4f7930ff..6e8a222e 100644 --- a/crypto/legacy_crypto_algorithm_test.go +++ b/crypto/legacy_cryptor_test.go @@ -8,18 +8,18 @@ import ( ) func legacyCanDecryptEncryptStreamResult(in []byte) bool { - cryptoAlgorithm, e := NewLegacyCryptoAlgorithm("enigma", true) + cryptor, e := NewLegacyCryptor("enigma", true) if e != nil { return false } - output, err := cryptoAlgorithm.EncryptStream(bytes.NewReader(in)) + output, err := cryptor.EncryptStream(bytes.NewReader(in)) if err != nil { return false } encryptedData, err := io.ReadAll(output.Reader) - decrypted, err := cryptoAlgorithm.Decrypt(&EncryptedData{ + decrypted, err := cryptor.Decrypt(&EncryptedData{ Data: encryptedData, Metadata: output.Metadata, }) @@ -30,7 +30,7 @@ func legacyCanDecryptEncryptStreamResult(in []byte) bool { } func legacyCanDecryptStreamEncryptResult(in []byte) bool { - cryptor, e := NewLegacyCryptoAlgorithm("enigma", true) + cryptor, e := NewLegacyCryptor("enigma", true) if e != nil { return false } diff --git a/crypto/module.go b/crypto/module.go new file mode 100644 index 00000000..a370eb05 --- /dev/null +++ b/crypto/module.go @@ -0,0 +1,131 @@ +package crypto + +import ( + "bufio" + "bytes" + "fmt" + "io" +) + +// CryptoModule is an interface for encrypting and decrypting data. +type CryptoModule interface { + Encrypt(input []byte) ([]byte, error) + Decrypt(input []byte) ([]byte, error) + EncryptStream(input io.Reader) (io.Reader, error) + DecryptStream(input io.Reader) (io.Reader, error) +} + +type module struct { + encryptor ExtendedCryptor + decryptors map[string]ExtendedCryptor +} + +func NewLegacyCryptoModule(cipherKey string, randomIv bool) (CryptoModule, error) { + legacy, e := NewLegacyCryptor(cipherKey, randomIv) + if e != nil { + return nil, e + } + aesCbc, e := NewAesCbcCryptor(cipherKey) + + if e != nil { + return nil, e + } + + return NewCryptoModule(legacy, []Cryptor{aesCbc}), nil +} + +func NewDefaultCryptoModule(cipherKey string, randomIv bool) (CryptoModule, error) { + aesCbc, e := NewAesCbcCryptor(cipherKey) + if e != nil { + return nil, e + } + + legacy, e := NewLegacyCryptor(cipherKey, randomIv) + if e != nil { + return nil, e + } + + return NewCryptoModule(aesCbc, []Cryptor{legacy}), nil +} + +func NewCryptoModule(defaultCryptor Cryptor, decryptors []Cryptor) CryptoModule { + + decryptorsMap := make(map[string]ExtendedCryptor, len(decryptors)+1) + for _, d := range decryptors { + decryptorsMap[d.Id()] = liftToExtendedCryptor(d) + } + + encryptor := liftToExtendedCryptor(defaultCryptor) + decryptorsMap[encryptor.Id()] = encryptor + + return &module{ + encryptor: encryptor, + decryptors: decryptorsMap, + } +} + +func (m *module) Encrypt(message []byte) ([]byte, error) { + encryptedData, e := m.encryptor.Encrypt(message) + if e != nil { + return nil, e + } + + if m.encryptor.Id() == legacyId { + return encryptedData.Data, nil + } + + header, e := headerV1(m.encryptor.Id(), encryptedData.Metadata) + if e != nil { + return nil, e + } + + return append(header, encryptedData.Data...), nil +} + +func (m *module) Decrypt(data []byte) ([]byte, error) { + id, encryptedData, e := parseHeader(data) + if e != nil { + return nil, e + } + + decryptor := m.decryptors[*id] + + if decryptor == nil { + return nil, fmt.Errorf("decryption error: unknown cryptor id %s", *id) + } + + return m.decryptors[*id].Decrypt(encryptedData) +} + +func (m *module) EncryptStream(input io.Reader) (io.Reader, error) { + encryptedStreamData, e := m.encryptor.EncryptStream(input) + if e != nil { + return nil, e + } + if m.encryptor.Id() == legacyId { + return encryptedStreamData.Reader, nil + } + header, e := headerV1(m.encryptor.Id(), encryptedStreamData.Metadata) + if e != nil { + return nil, e + } + + headerReader := bytes.NewReader(header) + + return io.MultiReader(headerReader, encryptedStreamData.Reader), nil +} + +func (m *module) DecryptStream(input io.Reader) (io.Reader, error) { + + id, encryptedStreamData, e := parseHeaderStream(bufio.NewReader(input)) + if e != nil { + return nil, e + } + decryptor := m.decryptors[*id] + + if decryptor == nil { + return nil, fmt.Errorf("decryption error: unknown cryptor id %s", *id) + } + + return m.decryptors[*id].DecryptStream(encryptedStreamData) +} diff --git a/crypto/utils.go b/crypto/utils.go index 0d3a3d9c..fdf9132e 100644 --- a/crypto/utils.go +++ b/crypto/utils.go @@ -57,5 +57,5 @@ func unpadPKCS7(data []byte) ([]byte, error) { } func unsupportedHeaderVersion(version int) error { - return fmt.Errorf("unknown cryptor error: unsupported crypto header version %d", version) + return fmt.Errorf("unknown crypto error: unsupported crypto header version %d", version) } diff --git a/tests/contract/contract_test.go b/tests/contract/contract_test.go index a4604bd4..d4bd06f7 100644 --- a/tests/contract/contract_test.go +++ b/tests/contract/contract_test.go @@ -14,7 +14,7 @@ var format string func TestMain(m *testing.M) { flag.StringVar(&path, "path", "../../../sdk-specifications/features", "Path to feature files") - flag.StringVar(&tagsFilter, "tagsFilter", "~@skip && ~@na=go && @beta && @featureSet=cryptorModule && @whatever", "Tags filter") + flag.StringVar(&tagsFilter, "tagsFilter", "~@skip && ~@na=go && @beta && @featureSet=cryptorModule", "Tags filter") flag.StringVar(&format, "format", "pretty", "Output formatter") flag.Parse() if path == "" { diff --git a/tests/contract/crypto_state_test.go b/tests/contract/crypto_state_test.go index eb0e4963..f72ad940 100644 --- a/tests/contract/crypto_state_test.go +++ b/tests/contract/crypto_state_test.go @@ -2,13 +2,15 @@ package contract import ( "context" + "github.com/pubnub/go/v7/crypto" "io" + "os" ) type cryptoStateKey struct{} type cryptoState struct { - cryptoAlgorithm string + cryptorNames []string cipherKey string cryptoFeaturePath string randomIv bool @@ -17,6 +19,23 @@ type cryptoState struct { err error } +func (c *cryptoState) createModule() (crypto.CryptoModule, error) { + cryptors := make([]crypto.Cryptor, len(c.cryptorNames)) + var e error + for i, cryptor := range c.cryptorNames { + cryptors[i], e = createCryptor(cryptor, c.cipherKey, c.randomIv) + if e != nil { + return nil, e + } + } + module := crypto.NewCryptoModule(cryptors[0], cryptors[1:]) + return module, nil +} + +func (c *cryptoState) openAssetFile(filename string) (io.ReadCloser, error) { + return os.Open(c.cryptoFeaturePath + "/assets/" + filename) +} + func getCryptoState(ctx context.Context) *cryptoState { return ctx.Value(cryptoStateKey{}).(*cryptoState) } diff --git a/tests/contract/crypto_steps_test.go b/tests/contract/crypto_steps_test.go index ce15963b..d6683694 100644 --- a/tests/contract/crypto_steps_test.go +++ b/tests/contract/crypto_steps_test.go @@ -6,21 +6,22 @@ import ( "context" "errors" "fmt" - "github.com/cucumber/godog" "github.com/pubnub/go/v7/crypto" "io" "os" "strings" ) -func cryptoAlgorithm(ctx context.Context, cryptoAlgorithm string) error { +func cryptor(ctx context.Context, cryptor string) error { cryptoState := getCryptoState(ctx) - cryptoState.cryptoAlgorithm = cryptoAlgorithm + cryptoState.cryptorNames = append(cryptoState.cryptorNames, cryptor) return nil } -func cryptorModuleWithRegisteredCryptoAlgorithms() error { - return godog.ErrPending +func cryptoModuleWithRegisteredcryptors(ctx context.Context, cryptor1 string, cryptor2 string) error { + cryptoState := getCryptoState(ctx) + cryptoState.cryptorNames = append(cryptoState.cryptorNames, cryptor1, cryptor2) + return nil } func decryptedFileContentEqualToFileContent(ctx context.Context, filename string) error { @@ -58,18 +59,21 @@ func encryptedFileSuccessfullyDecryptedByLegacyCodeWithCipherKeyAndVector(ctx co riv := randomIv(iv) cryptoState := getCryptoState(ctx) - algorithm, e := createCryptoAlgorithm("legacy", cipherKey, riv) + cryptor, e := createCryptor("legacy", cipherKey, riv) + if e != nil { + return e + } + module := crypto.NewCryptoModule(cryptor, []crypto.Cryptor{cryptor}) if e != nil { return e } - cryptor := crypto.NewCryptor(algorithm) if cryptoState.result != nil { - _, err := cryptor.Decrypt(cryptoState.result) + _, err := module.Decrypt(cryptoState.result) if err != nil { return err } } else if cryptoState.resultReader != nil { - _, err := cryptor.DecryptStream(bufio.NewReader(cryptoState.resultReader)) + _, err := module.DecryptStream(bufio.NewReader(cryptoState.resultReader)) if err != nil { return err } @@ -83,20 +87,17 @@ func encryptedFileSuccessfullyDecryptedByLegacyCodeWithCipherKeyAndVector(ctx co func iDecryptFileAs(ctx context.Context, filename string, decryptionType string) error { cryptoState := getCryptoState(ctx) - - algorithm, e := createCryptoAlgorithm(cryptoState.cryptoAlgorithm, cryptoState.cipherKey, cryptoState.randomIv) + module, e := cryptoState.createModule() if e != nil { return e } - - cryptor := crypto.NewCryptor(algorithm) file, e := os.Open(cryptoState.cryptoFeaturePath + "/assets/" + filename) if e != nil { return e } if decryptionType == "stream" { - cryptoState.resultReader, e = cryptor.DecryptStream(bufio.NewReader(file)) + cryptoState.resultReader, e = module.DecryptStream(bufio.NewReader(file)) if e != nil { return e } @@ -106,12 +107,11 @@ func iDecryptFileAs(ctx context.Context, filename string, decryptionType string) if e != nil { return e } - cryptoState.result, e = cryptor.Decrypt(fileContent) + cryptoState.result, e = module.Decrypt(fileContent) if e != nil { return e } } - return nil } @@ -119,18 +119,16 @@ func iDecryptFile(ctx context.Context, filename string) error { cryptoState := getCryptoState(ctx) - algorithm, e := createCryptoAlgorithm(cryptoState.cryptoAlgorithm, cryptoState.cipherKey, cryptoState.randomIv) + module, e := cryptoState.createModule() if e != nil { return e } - - cryptor := crypto.NewCryptor(algorithm) - file, e := os.Open(cryptoState.cryptoFeaturePath + "/assets/" + filename) + file, e := cryptoState.openAssetFile(filename) if e != nil { return e } - _, e = cryptor.DecryptStream(bufio.NewReader(file)) + _, e = module.DecryptStream(file) if e != nil { cryptoState.err = e } @@ -138,11 +136,11 @@ func iDecryptFile(ctx context.Context, filename string) error { return nil } -func createCryptoAlgorithm(name string, cipherKey string, randomIv bool) (crypto.CryptoAlgorithm, error) { +func createCryptor(name string, cipherKey string, randomIv bool) (crypto.Cryptor, error) { if name == "acrh" { - return crypto.NewAesCBCCryptoAlgorithm(cipherKey) + return crypto.NewAesCbcCryptor(cipherKey) } else if name == "legacy" { - return crypto.NewLegacyCryptoAlgorithm(cipherKey, randomIv) + return crypto.NewLegacyCryptor(cipherKey, randomIv) } else { return nil, fmt.Errorf("unknown crypto algorithm %s", name) } @@ -150,18 +148,16 @@ func createCryptoAlgorithm(name string, cipherKey string, randomIv bool) (crypto func iEncryptFileAs(ctx context.Context, filename string, encryptionType string) error { cryptoState := getCryptoState(ctx) - algorithm, e := createCryptoAlgorithm(cryptoState.cryptoAlgorithm, cryptoState.cipherKey, cryptoState.randomIv) + module, e := cryptoState.createModule() if e != nil { return e } - - cryptor := crypto.NewCryptor(algorithm) - file, e := os.Open(cryptoState.cryptoFeaturePath + "/assets/" + filename) + file, e := cryptoState.openAssetFile(filename) if e != nil { return e } if encryptionType == "stream" { - cryptoState.resultReader, e = cryptor.EncryptStream(bufio.NewReader(file)) + cryptoState.resultReader, e = module.EncryptStream(bufio.NewReader(file)) if e != nil { return e } @@ -170,12 +166,13 @@ func iEncryptFileAs(ctx context.Context, filename string, encryptionType string) if e != nil { return e } - cryptoState.result, e = cryptor.Encrypt(content) + cryptoState.result, e = module.Encrypt(content) if e != nil { return e } } + _ = file.Close() return nil } @@ -203,7 +200,7 @@ func iReceiveSuccess(ctx context.Context) error { func iReceiveUnknownCryptorError(ctx context.Context) error { cryptoState := getCryptoState(ctx) if cryptoState.err != nil { - if strings.Contains(cryptoState.err.Error(), "unknown cryptor error") { + if strings.Contains(cryptoState.err.Error(), "unknown crypto error") { return nil } else { return cryptoState.err diff --git a/tests/contract/steps_mapping_test.go b/tests/contract/steps_mapping_test.go index 1b4de34e..39d6e0bd 100644 --- a/tests/contract/steps_mapping_test.go +++ b/tests/contract/steps_mapping_test.go @@ -56,8 +56,8 @@ func MapSteps(ctx *godog.ScenarioContext) { ctx.Step(`^the result is successful$`, theResultIsSuccessful) ctx.Step(`^the token string \'(.*)\'$`, theTokenString) - ctx.Step(`^\'(.*)\' crypto algorithm$`, cryptoAlgorithm) - ctx.Step(`^cryptor module with registered \'(.*)\' and \'(.*)\' crypto algorithms$`, cryptorModuleWithRegisteredCryptoAlgorithms) + ctx.Step(`^\'(.*)\' crypto algorithm$`, cryptor) + ctx.Step(`^cryptor module with registered \'(.*)\' and \'(.*)\' crypto algorithms$`, cryptoModuleWithRegisteredcryptors) ctx.Step(`^Decrypted file content equal to the \'(.*)\' file content$`, decryptedFileContentEqualToFileContent) ctx.Step(`^Encrypted file successfully decrypted by legacy code with \'(.*)\' cipher key and \'(.*)\' vector$`, encryptedFileSuccessfullyDecryptedByLegacyCodeWithCipherKeyAndVector) ctx.Step(`^I decrypt \'(.*)\' file as \'(.*)\'$`, iDecryptFileAs) diff --git a/utils/crypto.go b/utils/crypto.go index e9875b82..a4dd8d5e 100644 --- a/utils/crypto.go +++ b/utils/crypto.go @@ -100,15 +100,15 @@ func GetHmacSha256(secretKey string, input string) string { // EncryptFile DEPRECATED func EncryptFile(cipherKey string, _ []byte, filePart io.Writer, file *os.File) { - cryptor, e := crypto.NewLegacyCryptoAlgorithm(cipherKey, true) + cryptoModule, e := crypto.NewLegacyCryptoModule(cipherKey, true) if e != nil { panic(e) } - r, e := cryptor.EncryptStream(file) + r, e := cryptoModule.EncryptStream(file) if e != nil { panic(e) } - _, e = io.Copy(filePart, r.Reader) + _, e = io.Copy(filePart, r) if e != nil { panic(e) } From e2f48e13aaebff8f14853aef98e830a8d6d8386d Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Fri, 15 Sep 2023 14:47:54 +0200 Subject: [PATCH 06/30] Fix old tests --- utils/crypto.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/utils/crypto.go b/utils/crypto.go index a4dd8d5e..2441ef41 100644 --- a/utils/crypto.go +++ b/utils/crypto.go @@ -54,7 +54,8 @@ func DecryptString(cipherKey string, message string, useRandomInitializationVect if e != nil { return nil, e } - return cryptoModule.Decrypt(value) + val, e := cryptoModule.Decrypt(value) + return fmt.Sprintf("%s", string(val)), e } // encodeNonAsciiChars creates unicode string of the non-ascii chars. @@ -100,11 +101,11 @@ func GetHmacSha256(secretKey string, input string) string { // EncryptFile DEPRECATED func EncryptFile(cipherKey string, _ []byte, filePart io.Writer, file *os.File) { - cryptoModule, e := crypto.NewLegacyCryptoModule(cipherKey, true) + cryptor, e := crypto.NewLegacyCryptoModule(cipherKey, true) if e != nil { panic(e) } - r, e := cryptoModule.EncryptStream(file) + r, e := cryptor.EncryptStream(file) if e != nil { panic(e) } From 5a9ce3f1ee62e192e7d6afd6c40b019e1fd8ecde Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Fri, 15 Sep 2023 14:58:25 +0200 Subject: [PATCH 07/30] Check for lenght shorter than sentinel --- crypto/cryptor_header.go | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/crypto/cryptor_header.go b/crypto/cryptor_header.go index 454833cc..858a43af 100644 --- a/crypto/cryptor_header.go +++ b/crypto/cryptor_header.go @@ -68,20 +68,8 @@ func headerV1(cryptorId string, metadata []byte) ([]byte, error) { return buffer.Bytes(), nil } -func slicesEqual(a, b []byte) bool { - if len(a) != len(b) { - return false - } - for i, v := range a { - if v != b[i] { - return false - } - } - return true -} - func peekHeaderCryptorId(data []byte) (cryptorId *string, e error) { - if !slicesEqual(data[:len(sentinel)], sentinel[:]) { + if len(data) < len(sentinel) || !bytes.Equal(data[:len(sentinel)], sentinel[:]) { return &legacyId, nil } From 10d198f60c702263212f16e1da8c20e7c836edca Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Fri, 15 Sep 2023 16:26:53 +0200 Subject: [PATCH 08/30] Use new crypto module in pubnub --- config.go | 50 ++++++++-------- files_download_file.go | 36 ++++++------ files_send_file_to_s3.go | 25 ++++++-- fire_request.go | 16 ++++-- publish_request.go | 18 +++--- pubnub.go | 13 ++++- subscription_manager.go | 15 +++-- utils.go | 120 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 220 insertions(+), 73 deletions(-) create mode 100644 utils.go diff --git a/config.go b/config.go index 67596b9e..d0bd6b69 100644 --- a/config.go +++ b/config.go @@ -2,6 +2,7 @@ package pubnub import ( "fmt" + "github.com/pubnub/go/v7/crypto" "log" "sync" ) @@ -27,29 +28,30 @@ type Config struct { // //Deprecated: please use SetUserId/GetUserId UUID string - CipherKey string // If CipherKey is passed, all communications to/from PubNub will be encrypted. - Secure bool // True to use TLS - ConnectTimeout int // net.Dialer.Timeout - NonSubscribeRequestTimeout int // http.Client.Timeout for non-subscribe requests - SubscribeRequestTimeout int // http.Client.Timeout for subscribe requests only - FileUploadRequestTimeout int // http.Client.Timeout File Upload Request only - HeartbeatInterval int // The frequency of the pings to the server to state that the client is active - PresenceTimeout int // The time after which the server will send a timeout for the client - MaximumReconnectionRetries int // The config sets how many times to retry to reconnect before giving up. - MaximumLatencyDataAge int // Max time to store the latency data for telemetry - FilterExpression string // Feature to subscribe with a custom filter expression. - PNReconnectionPolicy ReconnectionPolicy // Reconnection policy selection - Log *log.Logger // Logger instance - SuppressLeaveEvents bool // When true the SDK doesn't send out the leave requests. - DisablePNOtherProcessing bool // PNOther processing looks for pn_other in the JSON on the recevied message - UseHTTP2 bool // HTTP2 Flag - MessageQueueOverflowCount int // When the limit is exceeded by the number of messages received in a single subscribe request, a status event PNRequestMessageCountExceededCategory is fired. - MaxIdleConnsPerHost int // Used to set the value of HTTP Transport's MaxIdleConnsPerHost. - MaxWorkers int // Number of max workers for Publish and Grant requests - UsePAMV3 bool // Use PAM version 2, Objects requets would still use PAM v3 - StoreTokensOnGrant bool // Will store grant v3 tokens in token manager for further use. - FileMessagePublishRetryLimit int // The number of tries made in case of Publish File Message failure. - UseRandomInitializationVector bool // When true the IV will be random for all requests and not just file upload. When false the IV will be hardcoded for all requests except File Upload + CipherKey string // If CipherKey is passed, all communications to/from PubNub will be encrypted. + Secure bool // True to use TLS + ConnectTimeout int // net.Dialer.Timeout + NonSubscribeRequestTimeout int // http.Client.Timeout for non-subscribe requests + SubscribeRequestTimeout int // http.Client.Timeout for subscribe requests only + FileUploadRequestTimeout int // http.Client.Timeout File Upload Request only + HeartbeatInterval int // The frequency of the pings to the server to state that the client is active + PresenceTimeout int // The time after which the server will send a timeout for the client + MaximumReconnectionRetries int // The config sets how many times to retry to reconnect before giving up. + MaximumLatencyDataAge int // Max time to store the latency data for telemetry + FilterExpression string // Feature to subscribe with a custom filter expression. + PNReconnectionPolicy ReconnectionPolicy // Reconnection policy selection + Log *log.Logger // Logger instance + SuppressLeaveEvents bool // When true the SDK doesn't send out the leave requests. + DisablePNOtherProcessing bool // PNOther processing looks for pn_other in the JSON on the recevied message + UseHTTP2 bool // HTTP2 Flag + MessageQueueOverflowCount int // When the limit is exceeded by the number of messages received in a single subscribe request, a status event PNRequestMessageCountExceededCategory is fired. + MaxIdleConnsPerHost int // Used to set the value of HTTP Transport's MaxIdleConnsPerHost. + MaxWorkers int // Number of max workers for Publish and Grant requests + UsePAMV3 bool // Use PAM version 2, Objects requets would still use PAM v3 + StoreTokensOnGrant bool // Will store grant v3 tokens in token manager for further use. + FileMessagePublishRetryLimit int // The number of tries made in case of Publish File Message failure. + UseRandomInitializationVector bool // When true the IV will be random for all requests and not just file upload. When false the IV will be hardcoded for all requests except File Upload + CryptoModule crypto.CryptoModule //a cryptography module used for encryption and decryption } // NewDemoConfig initiates the config with demo keys, for tests only. @@ -90,7 +92,7 @@ func NewConfigWithUserId(userId UserId) *Config { return &c } -//Deprecated: Please use NewConfigWithUserId +// Deprecated: Please use NewConfigWithUserId func NewConfig(uuid string) *Config { return NewConfigWithUserId(UserId(uuid)) } diff --git a/files_download_file.go b/files_download_file.go index 5c3e51ef..0723cae2 100644 --- a/files_download_file.go +++ b/files_download_file.go @@ -2,12 +2,10 @@ package pubnub import ( "fmt" + "github.com/pubnub/go/v7/crypto" "io" "net/http" "net/url" - "strconv" - - "github.com/pubnub/go/v7/utils" ) var emptyDownloadFileResponse *PNDownloadFileResponse @@ -93,32 +91,30 @@ func (b *downloadFileBuilder) Execute() (*PNDownloadFileResponse, StatusResponse stat.StatusCode = resp.StatusCode return nil, stat, err } - contentLenEnc, err := strconv.ParseInt(string(resp.Header.Get("Content-Length")), 10, 64) - if err != nil { - b.opts.pubnub.Config.Log.Printf("err in parsing content length %s", err) - return nil, stat, err - } var respDL *PNDownloadFileResponse - if b.opts.CipherKey != "" { - r, w := io.Pipe() - utils.DecryptFile(b.opts.CipherKey, contentLenEnc, resp.Body, w) - respDL = &PNDownloadFileResponse{ - File: r, + if b.opts.CipherKey == "" && b.opts.pubnub.cryptoModule == nil { + + } else { + var e error + cryptoModule := b.opts.pubnub.cryptoModule + if b.opts.CipherKey != "" { + cryptoModule, e = crypto.NewLegacyCryptoModule(b.opts.CipherKey, true) + if e != nil { + return nil, stat, e + } } - } else if b.opts.pubnub.Config.CipherKey != "" { - r, w := io.Pipe() - utils.DecryptFile(b.opts.pubnub.Config.CipherKey, contentLenEnc, resp.Body, w) + r, e := cryptoModule.DecryptStream(resp.Body) + if e != nil { + return nil, stat, e + } respDL = &PNDownloadFileResponse{ File: r, } - } else { - respDL = &PNDownloadFileResponse{ - File: resp.Body, - } } + return respDL, stat, nil } diff --git a/files_send_file_to_s3.go b/files_send_file_to_s3.go index 442adca8..016f1a80 100644 --- a/files_send_file_to_s3.go +++ b/files_send_file_to_s3.go @@ -3,6 +3,7 @@ package pubnub import ( "bytes" "encoding/json" + "github.com/pubnub/go/v7/crypto" "io" "io/ioutil" "mime/multipart" @@ -11,7 +12,6 @@ import ( "os" "github.com/pubnub/go/v7/pnerr" - "github.com/pubnub/go/v7/utils" ) var emptySendFileToS3Response *PNSendFileToS3Response @@ -131,17 +131,30 @@ func (o *sendFileToS3Opts) buildBodyMultipartFileUpload() (bytes.Buffer, *multip return bytes.Buffer{}, writer, s, errFilePart } - if o.CipherKey != "" { - utils.EncryptFile(o.CipherKey, []byte{}, filePart, o.File) - } else if o.pubnub.Config.CipherKey != "" { - utils.EncryptFile(o.pubnub.Config.CipherKey, []byte{}, filePart, o.File) - } else { + if o.CipherKey == "" && o.pubnub.cryptoModule == nil { _, errIOCopy := io.Copy(filePart, o.File) if errIOCopy != nil { o.pubnub.Config.Log.Printf("ERROR: io Copy error: %s\n", errIOCopy.Error()) return bytes.Buffer{}, writer, s, errIOCopy } + } else { + var e error + cryptorModule := o.pubnub.cryptoModule + + if o.CipherKey != "" { + cryptorModule, e = crypto.NewLegacyCryptoModule(o.CipherKey, true) + if e != nil { + o.pubnub.Config.Log.Printf("ERROR: %s\n", e.Error()) + return bytes.Buffer{}, writer, s, e + } + } + + e = encryptStreamAndCopyTo(cryptorModule, o.File, filePart) + if e != nil { + o.pubnub.Config.Log.Printf("ERROR: %s\n", e.Error()) + return bytes.Buffer{}, writer, s, e + } } errWriterClose := writer.Close() diff --git a/fire_request.go b/fire_request.go index fbe0709d..580daf3e 100644 --- a/fire_request.go +++ b/fire_request.go @@ -157,8 +157,11 @@ func (o *fireOpts) buildPath() (string, error) { var message []byte var err error - if cipherKey := o.pubnub.Config.CipherKey; cipherKey != "" { - msg := utils.EncryptString(cipherKey, string(message), o.pubnub.Config.UseRandomInitializationVector) + if o.pubnub.cryptoModule != nil { + msg, e := encryptString(o.pubnub.cryptoModule, string(message)) + if e != nil { + return "", e + } o.Message = []byte(msg) } @@ -222,9 +225,12 @@ func (o *fireOpts) buildBody() ([]byte, error) { } } - if cipherKey := o.pubnub.Config.CipherKey; cipherKey != "" { - enc := utils.EncryptString(cipherKey, string(msg), o.pubnub.Config.UseRandomInitializationVector) - msg, err := utils.ValueAsString(enc) + if o.pubnub.cryptoModule != nil { + enc, err := encryptString(o.pubnub.cryptoModule, string(msg)) + if err != nil { + return []byte{}, err + } + msg, err := utils.ValueAsString(enc) //TODO WHAT?! if err != nil { return []byte{}, err } diff --git a/publish_request.go b/publish_request.go index 4b865d92..1dd88cce 100644 --- a/publish_request.go +++ b/publish_request.go @@ -198,13 +198,13 @@ func (o *publishOpts) validate() error { return nil } -func (o *publishOpts) encryptProcessing(cipherKey string) (string, error) { +func (o *publishOpts) encryptProcessing() (string, error) { var msg string var errJSONMarshal error o.pubnub.Config.Log.Println("EncryptString: encrypting", fmt.Sprintf("%s", o.Message)) if o.pubnub.Config.DisablePNOtherProcessing { - if msg, errJSONMarshal = utils.SerializeEncryptAndSerialize(o.Message, cipherKey, o.Serialize, o.pubnub.Config.UseRandomInitializationVector); errJSONMarshal != nil { + if msg, errJSONMarshal = serializeEncryptAndSerialize(o.pubnub.cryptoModule, o.Message, o.Serialize); errJSONMarshal != nil { o.pubnub.Config.Log.Printf("error in serializing: %v\n", errJSONMarshal) return "", errJSONMarshal } @@ -218,7 +218,7 @@ func (o *publishOpts) encryptProcessing(cipherKey string) (string, error) { if ok { o.pubnub.Config.Log.Println(ok, msgPart) - encMsg, errJSONMarshal := utils.SerializeAndEncrypt(msgPart, cipherKey, o.Serialize, o.pubnub.Config.UseRandomInitializationVector) + encMsg, errJSONMarshal := serializeAndEncrypt(o.pubnub.cryptoModule, msgPart, o.Serialize) if errJSONMarshal != nil { o.pubnub.Config.Log.Printf("error in serializing: %v\n", errJSONMarshal) return "", errJSONMarshal @@ -231,14 +231,14 @@ func (o *publishOpts) encryptProcessing(cipherKey string) (string, error) { } msg = string(jsonEncBytes) } else { - if msg, errJSONMarshal = utils.SerializeEncryptAndSerialize(o.Message, cipherKey, o.Serialize, o.pubnub.Config.UseRandomInitializationVector); errJSONMarshal != nil { + if msg, errJSONMarshal = serializeEncryptAndSerialize(o.pubnub.cryptoModule, o.Message, o.Serialize); errJSONMarshal != nil { o.pubnub.Config.Log.Printf("error in serializing: %v\n", errJSONMarshal) return "", errJSONMarshal } } break default: - if msg, errJSONMarshal = utils.SerializeEncryptAndSerialize(o.Message, cipherKey, o.Serialize, o.pubnub.Config.UseRandomInitializationVector); errJSONMarshal != nil { + if msg, errJSONMarshal = serializeEncryptAndSerialize(o.pubnub.cryptoModule, o.Message, o.Serialize); errJSONMarshal != nil { o.pubnub.Config.Log.Printf("error in serializing: %v\n", errJSONMarshal) return "", errJSONMarshal } @@ -261,8 +261,8 @@ func (o *publishOpts) buildPath() (string, error) { var msg string var errJSONMarshal error - if cipherKey := o.pubnub.Config.CipherKey; cipherKey != "" { - if msg, errJSONMarshal = o.encryptProcessing(cipherKey); errJSONMarshal != nil { + if o.pubnub.cryptoModule != nil { + if msg, errJSONMarshal = o.encryptProcessing(); errJSONMarshal != nil { return "", errJSONMarshal } @@ -336,8 +336,8 @@ func (o *publishOpts) buildQuery() (*url.Values, error) { func (o *publishOpts) buildBody() ([]byte, error) { if o.UsePost { - if cipherKey := o.pubnub.Config.CipherKey; cipherKey != "" { - msg, errJSONMarshal := o.encryptProcessing(cipherKey) + if o.pubnub.cryptoModule != nil { + msg, errJSONMarshal := o.encryptProcessing() if errJSONMarshal != nil { return []byte{}, errJSONMarshal } diff --git a/pubnub.go b/pubnub.go index 0f25bef9..fa5df1ea 100644 --- a/pubnub.go +++ b/pubnub.go @@ -2,6 +2,7 @@ package pubnub import ( "fmt" + "github.com/pubnub/go/v7/crypto" "io/ioutil" "log" "net/http" @@ -74,6 +75,7 @@ type PubNub struct { ctx Context cancel func() tokenManager *TokenManager + cryptoModule crypto.CryptoModule } // Publish is used to send a message to all subscribers of a channel. @@ -752,7 +754,7 @@ func NewPubNub(pnconf *Config) *PubNub { if pnconf.Log == nil { pnconf.Log = log.New(ioutil.Discard, "", log.Ldate|log.Ltime|log.Lshortfile) } - pnconf.Log.Println(fmt.Sprintf("PubNub Go v4 SDK: %s\npnconf: %v\n%s\n%s\n%s", Version, pnconf, runtime.Version(), runtime.GOARCH, runtime.GOOS)) + pnconf.Log.Println(fmt.Sprintf("PubNub Go v7 SDK: %s\npnconf: %v\n%s\n%s\n%s", Version, pnconf, runtime.Version(), runtime.GOARCH, runtime.GOOS)) utils.CheckUUID(pnconf.UUID) pn := &PubNub{ @@ -768,6 +770,15 @@ func NewPubNub(pnconf *Config) *PubNub { pn.jobQueue = make(chan *JobQItem) pn.requestWorkers = pn.newNonSubQueueProcessor(pnconf.MaxWorkers, ctx) pn.tokenManager = newTokenManager(pn, ctx) + if pnconf.CryptoModule != nil { + pn.cryptoModule = pnconf.CryptoModule + } else if pnconf.CipherKey != "" { + module, err := crypto.NewLegacyCryptoModule(pnconf.CipherKey, pnconf.UseRandomInitializationVector) + if err != nil { + panic(err) + } + pn.cryptoModule = module + } return pn } diff --git a/subscription_manager.go b/subscription_manager.go index 7672bfe5..8d019587 100644 --- a/subscription_manager.go +++ b/subscription_manager.go @@ -3,14 +3,13 @@ package pubnub import ( "encoding/json" "errors" + "github.com/pubnub/go/v7/crypto" "net/http" "reflect" "strconv" "strings" "sync" "time" - - "github.com/pubnub/go/v7/utils" ) // SubscriptionManager Events: @@ -662,7 +661,7 @@ func processNonPresencePayload(m *SubscriptionManager, payload subscribeMessage, m.listenerManager.announceMessageActionsEvent(pnMessageActionsEvent) case PNMessageTypeFile: var err error - messagePayload, err = parseCipherInterface(payload.Payload, m.pubnub.Config) + messagePayload, err = parseCipherInterface(payload.Payload, m.pubnub.Config, m.pubnub.cryptoModule) if err != nil { pnStatus := &PNStatus{ Category: PNBadRequestCategory, @@ -681,7 +680,7 @@ func processNonPresencePayload(m *SubscriptionManager, payload subscribeMessage, m.listenerManager.announceFile(pnFilesEvent) default: var err error - messagePayload, err = parseCipherInterface(payload.Payload, m.pubnub.Config) + messagePayload, err = parseCipherInterface(payload.Payload, m.pubnub.Config, m.pubnub.cryptoModule) if err != nil { pnStatus := &PNStatus{ Category: PNBadRequestCategory, @@ -959,8 +958,8 @@ func createPNMessageResult(messagePayload interface{}, actualCh, subscribedCh, c // cipherKey: cipher key to use to decrypt. // // returns the decrypted data as interface and error. -func parseCipherInterface(data interface{}, pnConf *Config) (interface{}, error) { - if pnConf.CipherKey != "" { +func parseCipherInterface(data interface{}, pnConf *Config, module crypto.CryptoModule) (interface{}, error) { + if module != nil { pnConf.Log.Println("reflect.TypeOf(data).Kind()", reflect.TypeOf(data).Kind(), data) switch v := data.(type) { case map[string]interface{}: @@ -970,7 +969,7 @@ func parseCipherInterface(data interface{}, pnConf *Config) (interface{}, error) msg, ok := v["pn_other"].(string) if ok { pnConf.Log.Println("v[pn_other]", v["pn_other"], v, msg) - decrypted, errDecryption := utils.DecryptString(pnConf.CipherKey, msg, pnConf.UseRandomInitializationVector) + decrypted, errDecryption := decryptString(module, msg) if errDecryption != nil { pnConf.Log.Println(errDecryption, msg) return v, errDecryption @@ -993,7 +992,7 @@ func parseCipherInterface(data interface{}, pnConf *Config) (interface{}, error) return v, nil case string: var intf interface{} - decrypted, errDecryption := utils.DecryptString(pnConf.CipherKey, data.(string), pnConf.UseRandomInitializationVector) + decrypted, errDecryption := decryptString(module, data.(string)) if errDecryption != nil { pnConf.Log.Println(errDecryption, intf) intf = data diff --git a/utils.go b/utils.go new file mode 100644 index 00000000..01aeeadd --- /dev/null +++ b/utils.go @@ -0,0 +1,120 @@ +package pubnub + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "github.com/pubnub/go/v7/crypto" + "github.com/pubnub/go/v7/pnerr" + "io" + "strconv" +) + +// encodeNonAsciiChars creates unicode string of the non-ascii chars. +// It accepts the following parameters: +// message: to parse. +// +// returns the encoded string. +func encodeNonASCIIChars(message string) string { + runeOfMessage := []rune(message) + lenOfRune := len(runeOfMessage) + encodedString := bytes.NewBuffer(make([]byte, 0, lenOfRune)) + for i := 0; i < lenOfRune; i++ { + intOfRune := uint16(runeOfMessage[i]) + if intOfRune > 127 { + hexOfRune := strconv.FormatUint(uint64(intOfRune), 16) + dataLen := len(hexOfRune) + paddingNum := 4 - dataLen + encodedString.WriteString(`\u`) + for i := 0; i < paddingNum; i++ { + encodedString.WriteString("0") + } + encodedString.WriteString(hexOfRune) + } else { + encodedString.WriteString(string(runeOfMessage[i])) + } + } + return encodedString.String() +} + +func encryptString(module crypto.CryptoModule, message string) (string, error) { + encryptedData, e := module.Encrypt([]byte(encodeNonASCIIChars(message))) + if e != nil { + return "", e + } + return base64.StdEncoding.EncodeToString(encryptedData), nil +} + +func serializeEncryptAndSerialize(cryptoModule crypto.CryptoModule, msg interface{}, serialize bool) (string, error) { + var encrypted string + var err error + + if serialize { + jsonSerialized, errJSONMarshal := json.Marshal(msg) + if errJSONMarshal != nil { + return "", errJSONMarshal + } + encrypted, err = encryptString(cryptoModule, string(jsonSerialized)) + + } else { + if serializedMsg, ok := msg.(string); ok { + encrypted, err = encryptString(cryptoModule, string(serializedMsg)) + } else { + return "", pnerr.NewBuildRequestError("Message is not JSON serialized.") + } + } + if err != nil { + return "", err + } + jsonSerialized, errJSONMarshal := json.Marshal(encrypted) + if errJSONMarshal != nil { + return "", errJSONMarshal + } + return string(jsonSerialized), nil +} + +func serializeAndEncrypt(cryptoModule crypto.CryptoModule, msg interface{}, serialize bool) (string, error) { + var encrypted string + var err error + if serialize { + jsonSerialized, errJSONMarshal := json.Marshal(msg) + if errJSONMarshal != nil { + return "", errJSONMarshal + } + encrypted, err = encryptString(cryptoModule, string(jsonSerialized)) + } else { + if serializedMsg, ok := msg.(string); ok { + encrypted, err = encryptString(cryptoModule, serializedMsg) + } else { + return "", pnerr.NewBuildRequestError("Message is not JSON serialized.") + } + } + if err != nil { + return "", err + } + + return encrypted, nil +} + +func encryptStreamAndCopyTo(module crypto.CryptoModule, reader io.Reader, writer io.Writer) error { + encryptedStream, e := module.EncryptStream(reader) + if e != nil { + return e + } + _, e = io.Copy(writer, encryptedStream) + if e != nil { + return e + } + return nil +} + +func decryptString(cryptoModule crypto.CryptoModule, message string) (retVal interface{}, err error) { + value, decodeErr := base64.StdEncoding.DecodeString(message) + if decodeErr != nil { + return "***decrypt error***", fmt.Errorf("decrypt error on decode: %s", decodeErr) + } + + val, e := cryptoModule.Decrypt(value) + return fmt.Sprintf("%s", string(val)), e +} From cc43c105d4c415073a6bf65a0c74b0dac29b8108 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Fri, 15 Sep 2023 16:40:27 +0200 Subject: [PATCH 09/30] Fix method calls --- fetch_request.go | 4 ++-- history_request.go | 4 ++-- publish_file_message.go | 4 ++-- subscription_manager_test.go | 36 ++++++++++++++++++------------------ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/fetch_request.go b/fetch_request.go index 93947617..1b0d5edc 100644 --- a/fetch_request.go +++ b/fetch_request.go @@ -278,7 +278,7 @@ func (o *fetchOpts) parseMessageActions(actions interface{}) map[string]PNHistor return resp } -//{"status": 200, "error": false, "error_message": "", "channels": {"ch1":[{"message_type": "", "message": {"text": "hey"}, "timetoken": "15959610984115342", "meta": "", "uuid": "db9c5e39-7c95-40f5-8d71-125765b6f561"}]}} +// {"status": 200, "error": false, "error_message": "", "channels": {"ch1":[{"message_type": "", "message": {"text": "hey"}, "timetoken": "15959610984115342", "meta": "", "uuid": "db9c5e39-7c95-40f5-8d71-125765b6f561"}]}} func (o *fetchOpts) fetchMessages(channels map[string]interface{}) map[string][]FetchResponseItem { messages := make(map[string][]FetchResponseItem, len(channels)) @@ -290,7 +290,7 @@ func (o *fetchOpts) fetchMessages(channels map[string]interface{}) map[string][] for _, val := range histResponseMap { if histResponse, ok3 := val.(map[string]interface{}); ok3 { - msg, _ := parseCipherInterface(histResponse["message"], o.pubnub.Config) + msg, _ := parseCipherInterface(histResponse["message"], o.pubnub.Config, o.pubnub.cryptoModule) histItem := FetchResponseItem{ Message: msg, diff --git a/history_request.go b/history_request.go index c6679c57..991df14c 100644 --- a/history_request.go +++ b/history_request.go @@ -224,7 +224,7 @@ func getHistoryItemsWithoutTimetoken(historyResponseRaw []byte, o *historyOpts, for i, v := range historyResponseItems { o.pubnub.Config.Log.Println(v) - items[i].Message, _ = parseCipherInterface(v, o.pubnub.Config) + items[i].Message, _ = parseCipherInterface(v, o.pubnub.Config, o.pubnub.cryptoModule) } return items, nil } @@ -237,7 +237,7 @@ func getHistoryItemsWithTimetoken(historyResponseItems []HistoryResponseItem, o for i, v := range historyResponseItems { if v.Message != nil { o.pubnub.Config.Log.Println(v.Message) - items[i].Message, _ = parseCipherInterface(v.Message, o.pubnub.Config) + items[i].Message, _ = parseCipherInterface(v.Message, o.pubnub.Config, o.pubnub.cryptoModule) o.pubnub.Config.Log.Println(v.Timetoken) items[i].Timetoken = v.Timetoken diff --git a/publish_file_message.go b/publish_file_message.go index eb4695ce..a0e5b0e8 100644 --- a/publish_file_message.go +++ b/publish_file_message.go @@ -201,7 +201,7 @@ func (o *publishFileMessageOpts) buildPath() (string, error) { } } - if cipherKey := o.pubnub.Config.CipherKey; cipherKey != "" { + if o.pubnub.cryptoModule != nil { var msg string var p *publishBuilder if o.context() != nil { @@ -211,7 +211,7 @@ func (o *publishFileMessageOpts) buildPath() (string, error) { } p.opts.Message = o.Message - msg, errJSONMarshal := p.opts.encryptProcessing(cipherKey) + msg, errJSONMarshal := p.opts.encryptProcessing() if errJSONMarshal != nil { return "", errJSONMarshal } diff --git a/subscription_manager_test.go b/subscription_manager_test.go index fa645598..af5a8b5c 100644 --- a/subscription_manager_test.go +++ b/subscription_manager_test.go @@ -22,7 +22,7 @@ func TestParseCipherInterfaceCipherWithCipher(t *testing.T) { pn.Config.CipherKey = "enigma" pn.Config.UseRandomInitializationVector = false - intf, err := parseCipherInterface(s, pn.Config) + intf, err := parseCipherInterface(s, pn.Config, pn.cryptoModule) assert.Nil(err) assert.Equal("yay!", intf.(string)) @@ -35,7 +35,7 @@ func TestParseCipherInterfacePlainWithCipher(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "enigma" - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) assert.Equal("yay!", intf.(string)) } @@ -47,7 +47,7 @@ func TestParseCipherInterfaceCipherWithDiffCipher(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "test" - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) assert.Equal("Wi24KS4pcTzvyuGOHubiXg==", intf.(string)) @@ -60,7 +60,7 @@ func TestParseCipherInterfacePlainWithDiffCipher(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "test" - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) assert.Equal("yay!", intf.(string)) } @@ -72,7 +72,7 @@ func TestParseCipherInterfaceCipherWithoutCipher(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "" - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) assert.Equal("Wi24KS4pcTzvyuGOHubiXg==", intf.(string)) } @@ -84,7 +84,7 @@ func TestParseCipherInterfacePlainWithoutCipher(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "" - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) assert.Equal("yay!", intf.(string)) } @@ -99,7 +99,7 @@ func TestParseCipherInterfacePlainWithCipherStruct(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "enigma" - intf, err := parseCipherInterface(s, pn.Config) + intf, err := parseCipherInterface(s, pn.Config, pn.cryptoModule) assert.Nil(err) if msg, ok := intf.(customStruct); !ok { @@ -121,7 +121,7 @@ func TestParseCipherInterfacePlainWithoutCipherStruct(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "" - intf, err := parseCipherInterface(s, pn.Config) + intf, err := parseCipherInterface(s, pn.Config, pn.cryptoModule) assert.Nil(err) if msg, ok := intf.(customStruct); !ok { @@ -148,7 +148,7 @@ func TestParseCipherInterfacePlainWithCipherMapPNOther(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "enigma" - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) msg := intf.(map[string]interface{}) assert.Equal("12345", msg["not_other"]) @@ -174,7 +174,7 @@ func TestParseCipherInterfacePlainWithoutCipherMapPNOther(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "" - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) msg := intf.(map[string]interface{}) assert.Equal("12345", msg["not_other"]) @@ -196,7 +196,7 @@ func TestParseCipherInterfaceCipherWithoutCipherStringPNOther(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "" - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) msg := intf.(map[string]interface{}) assert.Equal("1234", msg["not_other"]) @@ -219,7 +219,7 @@ func TestParseCipherInterfaceCipherWithCipherStringPNOther(t *testing.T) { pn.Config.CipherKey = "enigma" pn.Config.UseRandomInitializationVector = false - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) msg := intf.(map[string]interface{}) assert.Equal("1234", msg["not_other"]) @@ -238,7 +238,7 @@ func TestParseCipherInterfaceCipherWithoutCipherStruct(t *testing.T) { pn.Config.CipherKey = "enigma" pn.Config.UseRandomInitializationVector = false - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) msg := intf.(map[string]interface{}) assert.Equal("hi!", msg["Foo"]) @@ -256,7 +256,7 @@ func TestParseCipherInterfaceCipherWithCipherStructPNOther(t *testing.T) { pn.Config.CipherKey = "enigma" pn.Config.UseRandomInitializationVector = false - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) msg := intf.(map[string]interface{}) assert.Equal("1234", msg["not_other"]) @@ -278,7 +278,7 @@ func TestParseCipherInterfaceCipherWithOtherCipherStructPNOther(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "test" - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) msg := intf.(map[string]interface{}) assert.Equal("1234", msg["not_other"]) @@ -298,7 +298,7 @@ func TestParseCipherInterfaceCipherWithCipherStructPNOtherDisable(t *testing.T) pn.Config.UseRandomInitializationVector = false pn.Config.CipherKey = "enigma" - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) msg := intf.(map[string]interface{}) assert.Equal("1234", msg["not_other"]) @@ -314,7 +314,7 @@ func TestParseCipherInterfaceCipherWithIntSlice(t *testing.T) { pn.Config.DisablePNOtherProcessing = true pn.Config.CipherKey = "" - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) msg := intf.([]int) assert.Equal(1, msg[0]) @@ -329,7 +329,7 @@ func TestParseCipherInterfaceCipherWithoutCipherStruct2(t *testing.T) { pn.Config.CipherKey = "enigma" pn.Config.UseRandomInitializationVector = false - intf, _ := parseCipherInterface(s, pn.Config) + intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) msg := intf.(map[string]interface{}) assert.Equal("12345", msg["not_other"]) if msgOther, ok := msg["pn_other"].(map[string]interface{}); !ok { From 6af0c1669faa01062ef0ff8d00261f185fd7cb0b Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Mon, 18 Sep 2023 10:03:41 +0200 Subject: [PATCH 10/30] Support first mutation of config.CipherKey --- fetch_request.go | 2 +- files_send_file_to_s3.go | 8 ++++---- fire_request.go | 8 ++++---- history_request.go | 4 ++-- publish_file_message.go | 2 +- publish_request.go | 12 ++++++------ pubnub.go | 15 +++++++++++++++ subscription_manager_test.go | 36 ++++++++++++++++++------------------ 8 files changed, 51 insertions(+), 36 deletions(-) diff --git a/fetch_request.go b/fetch_request.go index 1b0d5edc..879c547b 100644 --- a/fetch_request.go +++ b/fetch_request.go @@ -290,7 +290,7 @@ func (o *fetchOpts) fetchMessages(channels map[string]interface{}) map[string][] for _, val := range histResponseMap { if histResponse, ok3 := val.(map[string]interface{}); ok3 { - msg, _ := parseCipherInterface(histResponse["message"], o.pubnub.Config, o.pubnub.cryptoModule) + msg, _ := parseCipherInterface(histResponse["message"], o.pubnub.Config, o.pubnub.getCryptoModule()) histItem := FetchResponseItem{ Message: msg, diff --git a/files_send_file_to_s3.go b/files_send_file_to_s3.go index 016f1a80..3d0df4a8 100644 --- a/files_send_file_to_s3.go +++ b/files_send_file_to_s3.go @@ -131,7 +131,7 @@ func (o *sendFileToS3Opts) buildBodyMultipartFileUpload() (bytes.Buffer, *multip return bytes.Buffer{}, writer, s, errFilePart } - if o.CipherKey == "" && o.pubnub.cryptoModule == nil { + if o.CipherKey == "" && o.pubnub.getCryptoModule() == nil { _, errIOCopy := io.Copy(filePart, o.File) if errIOCopy != nil { @@ -140,17 +140,17 @@ func (o *sendFileToS3Opts) buildBodyMultipartFileUpload() (bytes.Buffer, *multip } } else { var e error - cryptorModule := o.pubnub.cryptoModule + cryptoModule := o.pubnub.getCryptoModule() if o.CipherKey != "" { - cryptorModule, e = crypto.NewLegacyCryptoModule(o.CipherKey, true) + cryptoModule, e = crypto.NewLegacyCryptoModule(o.CipherKey, true) if e != nil { o.pubnub.Config.Log.Printf("ERROR: %s\n", e.Error()) return bytes.Buffer{}, writer, s, e } } - e = encryptStreamAndCopyTo(cryptorModule, o.File, filePart) + e = encryptStreamAndCopyTo(cryptoModule, o.File, filePart) if e != nil { o.pubnub.Config.Log.Printf("ERROR: %s\n", e.Error()) return bytes.Buffer{}, writer, s, e diff --git a/fire_request.go b/fire_request.go index 580daf3e..51b13e48 100644 --- a/fire_request.go +++ b/fire_request.go @@ -157,8 +157,8 @@ func (o *fireOpts) buildPath() (string, error) { var message []byte var err error - if o.pubnub.cryptoModule != nil { - msg, e := encryptString(o.pubnub.cryptoModule, string(message)) + if o.pubnub.getCryptoModule() != nil { + msg, e := encryptString(o.pubnub.getCryptoModule(), string(message)) if e != nil { return "", e } @@ -225,8 +225,8 @@ func (o *fireOpts) buildBody() ([]byte, error) { } } - if o.pubnub.cryptoModule != nil { - enc, err := encryptString(o.pubnub.cryptoModule, string(msg)) + if o.pubnub.getCryptoModule() != nil { + enc, err := encryptString(o.pubnub.getCryptoModule(), string(msg)) if err != nil { return []byte{}, err } diff --git a/history_request.go b/history_request.go index 991df14c..5c51e51d 100644 --- a/history_request.go +++ b/history_request.go @@ -224,7 +224,7 @@ func getHistoryItemsWithoutTimetoken(historyResponseRaw []byte, o *historyOpts, for i, v := range historyResponseItems { o.pubnub.Config.Log.Println(v) - items[i].Message, _ = parseCipherInterface(v, o.pubnub.Config, o.pubnub.cryptoModule) + items[i].Message, _ = parseCipherInterface(v, o.pubnub.Config, o.pubnub.getCryptoModule()) } return items, nil } @@ -237,7 +237,7 @@ func getHistoryItemsWithTimetoken(historyResponseItems []HistoryResponseItem, o for i, v := range historyResponseItems { if v.Message != nil { o.pubnub.Config.Log.Println(v.Message) - items[i].Message, _ = parseCipherInterface(v.Message, o.pubnub.Config, o.pubnub.cryptoModule) + items[i].Message, _ = parseCipherInterface(v.Message, o.pubnub.Config, o.pubnub.getCryptoModule()) o.pubnub.Config.Log.Println(v.Timetoken) items[i].Timetoken = v.Timetoken diff --git a/publish_file_message.go b/publish_file_message.go index a0e5b0e8..53cceb23 100644 --- a/publish_file_message.go +++ b/publish_file_message.go @@ -201,7 +201,7 @@ func (o *publishFileMessageOpts) buildPath() (string, error) { } } - if o.pubnub.cryptoModule != nil { + if o.pubnub.getCryptoModule() != nil { var msg string var p *publishBuilder if o.context() != nil { diff --git a/publish_request.go b/publish_request.go index 1dd88cce..c625f3e5 100644 --- a/publish_request.go +++ b/publish_request.go @@ -204,7 +204,7 @@ func (o *publishOpts) encryptProcessing() (string, error) { o.pubnub.Config.Log.Println("EncryptString: encrypting", fmt.Sprintf("%s", o.Message)) if o.pubnub.Config.DisablePNOtherProcessing { - if msg, errJSONMarshal = serializeEncryptAndSerialize(o.pubnub.cryptoModule, o.Message, o.Serialize); errJSONMarshal != nil { + if msg, errJSONMarshal = serializeEncryptAndSerialize(o.pubnub.getCryptoModule(), o.Message, o.Serialize); errJSONMarshal != nil { o.pubnub.Config.Log.Printf("error in serializing: %v\n", errJSONMarshal) return "", errJSONMarshal } @@ -218,7 +218,7 @@ func (o *publishOpts) encryptProcessing() (string, error) { if ok { o.pubnub.Config.Log.Println(ok, msgPart) - encMsg, errJSONMarshal := serializeAndEncrypt(o.pubnub.cryptoModule, msgPart, o.Serialize) + encMsg, errJSONMarshal := serializeAndEncrypt(o.pubnub.getCryptoModule(), msgPart, o.Serialize) if errJSONMarshal != nil { o.pubnub.Config.Log.Printf("error in serializing: %v\n", errJSONMarshal) return "", errJSONMarshal @@ -231,14 +231,14 @@ func (o *publishOpts) encryptProcessing() (string, error) { } msg = string(jsonEncBytes) } else { - if msg, errJSONMarshal = serializeEncryptAndSerialize(o.pubnub.cryptoModule, o.Message, o.Serialize); errJSONMarshal != nil { + if msg, errJSONMarshal = serializeEncryptAndSerialize(o.pubnub.getCryptoModule(), o.Message, o.Serialize); errJSONMarshal != nil { o.pubnub.Config.Log.Printf("error in serializing: %v\n", errJSONMarshal) return "", errJSONMarshal } } break default: - if msg, errJSONMarshal = serializeEncryptAndSerialize(o.pubnub.cryptoModule, o.Message, o.Serialize); errJSONMarshal != nil { + if msg, errJSONMarshal = serializeEncryptAndSerialize(o.pubnub.getCryptoModule(), o.Message, o.Serialize); errJSONMarshal != nil { o.pubnub.Config.Log.Printf("error in serializing: %v\n", errJSONMarshal) return "", errJSONMarshal } @@ -261,7 +261,7 @@ func (o *publishOpts) buildPath() (string, error) { var msg string var errJSONMarshal error - if o.pubnub.cryptoModule != nil { + if o.pubnub.getCryptoModule() != nil { if msg, errJSONMarshal = o.encryptProcessing(); errJSONMarshal != nil { return "", errJSONMarshal } @@ -336,7 +336,7 @@ func (o *publishOpts) buildQuery() (*url.Values, error) { func (o *publishOpts) buildBody() ([]byte, error) { if o.UsePost { - if o.pubnub.cryptoModule != nil { + if o.pubnub.getCryptoModule() != nil { msg, errJSONMarshal := o.encryptProcessing() if errJSONMarshal != nil { return []byte{}, errJSONMarshal diff --git a/pubnub.go b/pubnub.go index fa5df1ea..db59bfa2 100644 --- a/pubnub.go +++ b/pubnub.go @@ -76,6 +76,21 @@ type PubNub struct { cancel func() tokenManager *TokenManager cryptoModule crypto.CryptoModule + cryptoFirstSetupDone bool +} + +func (pn *PubNub) getCryptoModule() crypto.CryptoModule { + if pn.cryptoModule != nil { + return pn.cryptoModule + } + pn.Lock() + defer pn.Unlock() + if !pn.cryptoFirstSetupDone && (pn.Config != nil) && (pn.Config.CipherKey != "") { + pn.cryptoFirstSetupDone = true + pn.cryptoModule, _ = crypto.NewLegacyCryptoModule(pn.Config.CipherKey, pn.Config.UseRandomInitializationVector) + return pn.cryptoModule + } + return nil } // Publish is used to send a message to all subscribers of a channel. diff --git a/subscription_manager_test.go b/subscription_manager_test.go index af5a8b5c..af956a08 100644 --- a/subscription_manager_test.go +++ b/subscription_manager_test.go @@ -22,7 +22,7 @@ func TestParseCipherInterfaceCipherWithCipher(t *testing.T) { pn.Config.CipherKey = "enigma" pn.Config.UseRandomInitializationVector = false - intf, err := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, err := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) assert.Nil(err) assert.Equal("yay!", intf.(string)) @@ -35,7 +35,7 @@ func TestParseCipherInterfacePlainWithCipher(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "enigma" - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) assert.Equal("yay!", intf.(string)) } @@ -47,7 +47,7 @@ func TestParseCipherInterfaceCipherWithDiffCipher(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "test" - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) assert.Equal("Wi24KS4pcTzvyuGOHubiXg==", intf.(string)) @@ -60,7 +60,7 @@ func TestParseCipherInterfacePlainWithDiffCipher(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "test" - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) assert.Equal("yay!", intf.(string)) } @@ -72,7 +72,7 @@ func TestParseCipherInterfaceCipherWithoutCipher(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "" - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) assert.Equal("Wi24KS4pcTzvyuGOHubiXg==", intf.(string)) } @@ -84,7 +84,7 @@ func TestParseCipherInterfacePlainWithoutCipher(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "" - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) assert.Equal("yay!", intf.(string)) } @@ -99,7 +99,7 @@ func TestParseCipherInterfacePlainWithCipherStruct(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "enigma" - intf, err := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, err := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) assert.Nil(err) if msg, ok := intf.(customStruct); !ok { @@ -121,7 +121,7 @@ func TestParseCipherInterfacePlainWithoutCipherStruct(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "" - intf, err := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, err := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) assert.Nil(err) if msg, ok := intf.(customStruct); !ok { @@ -148,7 +148,7 @@ func TestParseCipherInterfacePlainWithCipherMapPNOther(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "enigma" - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) msg := intf.(map[string]interface{}) assert.Equal("12345", msg["not_other"]) @@ -174,7 +174,7 @@ func TestParseCipherInterfacePlainWithoutCipherMapPNOther(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "" - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) msg := intf.(map[string]interface{}) assert.Equal("12345", msg["not_other"]) @@ -196,7 +196,7 @@ func TestParseCipherInterfaceCipherWithoutCipherStringPNOther(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "" - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) msg := intf.(map[string]interface{}) assert.Equal("1234", msg["not_other"]) @@ -219,7 +219,7 @@ func TestParseCipherInterfaceCipherWithCipherStringPNOther(t *testing.T) { pn.Config.CipherKey = "enigma" pn.Config.UseRandomInitializationVector = false - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) msg := intf.(map[string]interface{}) assert.Equal("1234", msg["not_other"]) @@ -238,7 +238,7 @@ func TestParseCipherInterfaceCipherWithoutCipherStruct(t *testing.T) { pn.Config.CipherKey = "enigma" pn.Config.UseRandomInitializationVector = false - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) msg := intf.(map[string]interface{}) assert.Equal("hi!", msg["Foo"]) @@ -256,7 +256,7 @@ func TestParseCipherInterfaceCipherWithCipherStructPNOther(t *testing.T) { pn.Config.CipherKey = "enigma" pn.Config.UseRandomInitializationVector = false - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) msg := intf.(map[string]interface{}) assert.Equal("1234", msg["not_other"]) @@ -278,7 +278,7 @@ func TestParseCipherInterfaceCipherWithOtherCipherStructPNOther(t *testing.T) { pn := NewPubNub(NewDemoConfig()) pn.Config.CipherKey = "test" - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) msg := intf.(map[string]interface{}) assert.Equal("1234", msg["not_other"]) @@ -298,7 +298,7 @@ func TestParseCipherInterfaceCipherWithCipherStructPNOtherDisable(t *testing.T) pn.Config.UseRandomInitializationVector = false pn.Config.CipherKey = "enigma" - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) msg := intf.(map[string]interface{}) assert.Equal("1234", msg["not_other"]) @@ -314,7 +314,7 @@ func TestParseCipherInterfaceCipherWithIntSlice(t *testing.T) { pn.Config.DisablePNOtherProcessing = true pn.Config.CipherKey = "" - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) msg := intf.([]int) assert.Equal(1, msg[0]) @@ -329,7 +329,7 @@ func TestParseCipherInterfaceCipherWithoutCipherStruct2(t *testing.T) { pn.Config.CipherKey = "enigma" pn.Config.UseRandomInitializationVector = false - intf, _ := parseCipherInterface(s, pn.Config, pn.cryptoModule) + intf, _ := parseCipherInterface(s, pn.Config, pn.getCryptoModule()) msg := intf.(map[string]interface{}) assert.Equal("12345", msg["not_other"]) if msgOther, ok := msg["pn_other"].(map[string]interface{}); !ok { From 555db4500b269a6f9715b13ed4167d047cf1de19 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Mon, 18 Sep 2023 16:21:50 +0200 Subject: [PATCH 11/30] Fixes to contract tests --- crypto/aes_cbc_cryptor.go | 4 +- crypto/aes_cbc_cryptor_test.go | 4 +- crypto/cryptor_header.go | 2 +- crypto/decrypting_reader.go | 2 +- crypto/decrypting_reader_test.go | 2 +- crypto/encrypting_reader.go | 2 +- crypto/encrypting_reader_test.go | 2 +- crypto/legacy_cryptor.go | 4 +- crypto/legacy_cryptor_test.go | 4 +- crypto/module.go | 6 +-- files_download_file.go | 2 +- pubnub.go | 16 +++++--- tests/contract/contract_test.go | 2 +- tests/contract/crypto_state_test.go | 41 ++++++++++++--------- tests/contract/crypto_steps_test.go | 55 ++++++++++++++++++++++++---- tests/contract/steps_mapping_test.go | 11 ++++-- 16 files changed, 107 insertions(+), 52 deletions(-) diff --git a/crypto/aes_cbc_cryptor.go b/crypto/aes_cbc_cryptor.go index 229e04d2..e09712cd 100644 --- a/crypto/aes_cbc_cryptor.go +++ b/crypto/aes_cbc_cryptor.go @@ -68,7 +68,7 @@ func (c *aesCbcCryptor) EncryptStream(reader io.Reader) (*EncryptedStreamData, e return &EncryptedStreamData{ Metadata: iv, - Reader: NewBlockModeEncryptingReader(reader, cipher.NewCBCEncrypter(c.block, iv)), + Reader: newBlockModeEncryptingReader(reader, cipher.NewCBCEncrypter(c.block, iv)), }, nil } @@ -76,7 +76,7 @@ func (c *aesCbcCryptor) DecryptStream(encryptedData *EncryptedStreamData) (io.Re if encryptedData.Metadata == nil { return nil, errors.New("missing metadata") } - return NewBlockModeDecryptingReader(encryptedData.Reader, cipher.NewCBCDecrypter(c.block, encryptedData.Metadata)), nil + return newBlockModeDecryptingReader(encryptedData.Reader, cipher.NewCBCDecrypter(c.block, encryptedData.Metadata)), nil } func aesCipher(cipherKey string) (cipher.Block, error) { diff --git a/crypto/aes_cbc_cryptor_test.go b/crypto/aes_cbc_cryptor_test.go index 108ce130..a6b47b15 100644 --- a/crypto/aes_cbc_cryptor_test.go +++ b/crypto/aes_cbc_cryptor_test.go @@ -18,10 +18,10 @@ func canDecryptEncryptStreamResult(in []byte) bool { return false } - encryptedData, err := io.ReadAll(output.Reader) + encrData, err := io.ReadAll(output.Reader) decrypted, err := cryptor.Decrypt(&EncryptedData{ - Data: encryptedData, + Data: encrData, Metadata: output.Metadata, }) if err != nil { diff --git a/crypto/cryptor_header.go b/crypto/cryptor_header.go index 858a43af..b6dd3bb8 100644 --- a/crypto/cryptor_header.go +++ b/crypto/cryptor_header.go @@ -81,7 +81,7 @@ func peekHeaderCryptorId(data []byte) (cryptorId *string, e error) { return &id, nil } -func parseHeader(data []byte) (cryptorId *string, encryptedData *EncryptedData, e error) { +func parseHeader(data []byte) (cryptorId *string, encrData *EncryptedData, e error) { id, err := peekHeaderCryptorId(data) if err != nil { return nil, nil, err diff --git a/crypto/decrypting_reader.go b/crypto/decrypting_reader.go index 2ebe412d..2624beb1 100644 --- a/crypto/decrypting_reader.go +++ b/crypto/decrypting_reader.go @@ -8,7 +8,7 @@ import ( "io" ) -func NewBlockModeDecryptingReader(r io.Reader, mode cipher.BlockMode) io.Reader { +func newBlockModeDecryptingReader(r io.Reader, mode cipher.BlockMode) io.Reader { return &blockModeDecryptingReader{ r: bufio.NewReader(r), blockMode: mode, diff --git a/crypto/decrypting_reader_test.go b/crypto/decrypting_reader_test.go index 40d07463..949d3fe9 100644 --- a/crypto/decrypting_reader_test.go +++ b/crypto/decrypting_reader_test.go @@ -22,7 +22,7 @@ func canReadDifferentSizeOfChunks(in []byte, bufferSize uint8) bool { if bufferSize == 0 { return true } - decryptingReader := NewBlockModeDecryptingReader(bytes.NewReader(inPadded), &DoNothingBlockMode{}) + decryptingReader := newBlockModeDecryptingReader(bytes.NewReader(inPadded), &DoNothingBlockMode{}) readDataBuffer := bytes.NewBuffer(nil) buffer := make([]byte, bufferSize) numberOfReadBytes := 0 diff --git a/crypto/encrypting_reader.go b/crypto/encrypting_reader.go index d807530f..f8530825 100644 --- a/crypto/encrypting_reader.go +++ b/crypto/encrypting_reader.go @@ -8,7 +8,7 @@ import ( "io" ) -func NewBlockModeEncryptingReader(r io.Reader, mode cipher.BlockMode) io.Reader { +func newBlockModeEncryptingReader(r io.Reader, mode cipher.BlockMode) io.Reader { return &blockModeEncryptingReader{ r: bufio.NewReader(r), blockMode: mode, diff --git a/crypto/encrypting_reader_test.go b/crypto/encrypting_reader_test.go index dc711dce..fd76a057 100644 --- a/crypto/encrypting_reader_test.go +++ b/crypto/encrypting_reader_test.go @@ -11,7 +11,7 @@ func encryptingReaderCanReadDifferentSizeOfChunks(in []byte, bufferSize uint8) b if bufferSize == 0 { return true } - encryptingReader := NewBlockModeEncryptingReader(bytes.NewReader(in), &DoNothingBlockMode{}) + encryptingReader := newBlockModeEncryptingReader(bytes.NewReader(in), &DoNothingBlockMode{}) readDataBuffer := bytes.NewBuffer(nil) buffer := make([]byte, bufferSize) numberOfReadBytes := 0 diff --git a/crypto/legacy_cryptor.go b/crypto/legacy_cryptor.go index 5e51758b..b76daeee 100644 --- a/crypto/legacy_cryptor.go +++ b/crypto/legacy_cryptor.go @@ -88,7 +88,7 @@ func (c *legacyCryptor) EncryptStream(reader io.Reader) (*EncryptedStreamData, e return &EncryptedStreamData{ Metadata: nil, - Reader: io.MultiReader(bytes.NewReader(iv), NewBlockModeEncryptingReader(reader, cipher.NewCBCEncrypter(c.block, iv))), + Reader: io.MultiReader(bytes.NewReader(iv), newBlockModeEncryptingReader(reader, cipher.NewCBCEncrypter(c.block, iv))), }, nil } @@ -99,7 +99,7 @@ func (c *legacyCryptor) DecryptStream(encryptedData *EncryptedStreamData) (io.Re return nil, err } - return NewBlockModeDecryptingReader(encryptedData.Reader, cipher.NewCBCDecrypter(c.block, iv)), nil + return newBlockModeDecryptingReader(encryptedData.Reader, cipher.NewCBCDecrypter(c.block, iv)), nil } // EncryptCipherKey DEPRECATED diff --git a/crypto/legacy_cryptor_test.go b/crypto/legacy_cryptor_test.go index 6e8a222e..0db1f83b 100644 --- a/crypto/legacy_cryptor_test.go +++ b/crypto/legacy_cryptor_test.go @@ -17,10 +17,10 @@ func legacyCanDecryptEncryptStreamResult(in []byte) bool { return false } - encryptedData, err := io.ReadAll(output.Reader) + encr, err := io.ReadAll(output.Reader) decrypted, err := cryptor.Decrypt(&EncryptedData{ - Data: encryptedData, + Data: encr, Metadata: output.Metadata, }) if err != nil { diff --git a/crypto/module.go b/crypto/module.go index a370eb05..3cd7922a 100644 --- a/crypto/module.go +++ b/crypto/module.go @@ -34,7 +34,7 @@ func NewLegacyCryptoModule(cipherKey string, randomIv bool) (CryptoModule, error return NewCryptoModule(legacy, []Cryptor{aesCbc}), nil } -func NewDefaultCryptoModule(cipherKey string, randomIv bool) (CryptoModule, error) { +func NewAesCbcCryptoModule(cipherKey string, randomIv bool) (CryptoModule, error) { aesCbc, e := NewAesCbcCryptor(cipherKey) if e != nil { return nil, e @@ -91,7 +91,7 @@ func (m *module) Decrypt(data []byte) ([]byte, error) { decryptor := m.decryptors[*id] if decryptor == nil { - return nil, fmt.Errorf("decryption error: unknown cryptor id %s", *id) + return nil, fmt.Errorf("unknown crypto error: unknown cryptor id %s", *id) } return m.decryptors[*id].Decrypt(encryptedData) @@ -124,7 +124,7 @@ func (m *module) DecryptStream(input io.Reader) (io.Reader, error) { decryptor := m.decryptors[*id] if decryptor == nil { - return nil, fmt.Errorf("decryption error: unknown cryptor id %s", *id) + return nil, fmt.Errorf("unknown crypto error: unknown cryptor id %s", *id) } return m.decryptors[*id].DecryptStream(encryptedStreamData) diff --git a/files_download_file.go b/files_download_file.go index 0723cae2..99913a29 100644 --- a/files_download_file.go +++ b/files_download_file.go @@ -97,7 +97,7 @@ func (b *downloadFileBuilder) Execute() (*PNDownloadFileResponse, StatusResponse } else { var e error - cryptoModule := b.opts.pubnub.cryptoModule + cryptoModule := b.opts.pubnub.getCryptoModule() if b.opts.CipherKey != "" { cryptoModule, e = crypto.NewLegacyCryptoModule(b.opts.CipherKey, true) if e != nil { diff --git a/pubnub.go b/pubnub.go index db59bfa2..18f15423 100644 --- a/pubnub.go +++ b/pubnub.go @@ -76,19 +76,23 @@ type PubNub struct { cancel func() tokenManager *TokenManager cryptoModule crypto.CryptoModule - cryptoFirstSetupDone bool + previousCipherKey string + previousIvFlag bool } func (pn *PubNub) getCryptoModule() crypto.CryptoModule { - if pn.cryptoModule != nil { - return pn.cryptoModule - } pn.Lock() defer pn.Unlock() - if !pn.cryptoFirstSetupDone && (pn.Config != nil) && (pn.Config.CipherKey != "") { - pn.cryptoFirstSetupDone = true + if pn.previousCipherKey == pn.Config.CipherKey && pn.previousIvFlag == pn.Config.UseRandomInitializationVector { + return pn.cryptoModule + } + + if pn.Config != nil && pn.Config.CipherKey != "" { pn.cryptoModule, _ = crypto.NewLegacyCryptoModule(pn.Config.CipherKey, pn.Config.UseRandomInitializationVector) return pn.cryptoModule + } else if pn.Config != nil && pn.Config.CipherKey == "" { + pn.cryptoModule = nil + return pn.cryptoModule } return nil } diff --git a/tests/contract/contract_test.go b/tests/contract/contract_test.go index d4bd06f7..e02453c6 100644 --- a/tests/contract/contract_test.go +++ b/tests/contract/contract_test.go @@ -14,7 +14,7 @@ var format string func TestMain(m *testing.M) { flag.StringVar(&path, "path", "../../../sdk-specifications/features", "Path to feature files") - flag.StringVar(&tagsFilter, "tagsFilter", "~@skip && ~@na=go && @beta && @featureSet=cryptorModule", "Tags filter") + flag.StringVar(&tagsFilter, "tagsFilter", "~@skip && ~@na=go && ~@beta", "Tags filter") flag.StringVar(&format, "format", "pretty", "Output formatter") flag.Parse() if path == "" { diff --git a/tests/contract/crypto_state_test.go b/tests/contract/crypto_state_test.go index f72ad940..0cf7ff94 100644 --- a/tests/contract/crypto_state_test.go +++ b/tests/contract/crypto_state_test.go @@ -2,6 +2,7 @@ package contract import ( "context" + "errors" "github.com/pubnub/go/v7/crypto" "io" "os" @@ -10,26 +11,32 @@ import ( type cryptoStateKey struct{} type cryptoState struct { - cryptorNames []string - cipherKey string - cryptoFeaturePath string - randomIv bool - result []byte - resultReader io.Reader - err error + cryptorNames []string + cryptorName string + cipherKey string + cryptoFeaturePath string + randomIv bool + result []byte + resultReader io.Reader + err error + cryptoModule crypto.CryptoModule + legacyCodeCipherKey string + legacyCodeRandomIv bool } -func (c *cryptoState) createModule() (crypto.CryptoModule, error) { - cryptors := make([]crypto.Cryptor, len(c.cryptorNames)) - var e error - for i, cryptor := range c.cryptorNames { - cryptors[i], e = createCryptor(cryptor, c.cipherKey, c.randomIv) - if e != nil { - return nil, e - } +func (c *cryptoState) getCryptoModule() (crypto.CryptoModule, error) { + + if len(c.cryptorNames) > 0 && c.cryptorNames[0] == "legacy" { + return crypto.NewLegacyCryptoModule(c.cipherKey, c.randomIv) + } else if len(c.cryptorNames) > 0 && c.cryptorNames[0] == "acrh" { + return crypto.NewAesCbcCryptoModule(c.cipherKey, c.randomIv) + } else if c.cryptorName != "" { + cryptor, e := createCryptor(c.cryptorName, c.cipherKey, c.randomIv) + c.cryptoModule = crypto.NewCryptoModule(cryptor, []crypto.Cryptor{}) + return c.cryptoModule, e + } else { + return nil, errors.New("I don't know how to create this crypto module") } - module := crypto.NewCryptoModule(cryptors[0], cryptors[1:]) - return module, nil } func (c *cryptoState) openAssetFile(filename string) (io.ReadCloser, error) { diff --git a/tests/contract/crypto_steps_test.go b/tests/contract/crypto_steps_test.go index d6683694..1b4cc788 100644 --- a/tests/contract/crypto_steps_test.go +++ b/tests/contract/crypto_steps_test.go @@ -4,9 +4,11 @@ import ( "bufio" "bytes" "context" + "encoding/base64" "errors" "fmt" "github.com/pubnub/go/v7/crypto" + "github.com/pubnub/go/v7/utils" "io" "os" "strings" @@ -14,11 +16,11 @@ import ( func cryptor(ctx context.Context, cryptor string) error { cryptoState := getCryptoState(ctx) - cryptoState.cryptorNames = append(cryptoState.cryptorNames, cryptor) + cryptoState.cryptorName = cryptor return nil } -func cryptoModuleWithRegisteredcryptors(ctx context.Context, cryptor1 string, cryptor2 string) error { +func cryptoModuleWithDefaultAndAdditionalLegacyCryptors(ctx context.Context, cryptor1 string, cryptor2 string) error { cryptoState := getCryptoState(ctx) cryptoState.cryptorNames = append(cryptoState.cryptorNames, cryptor1, cryptor2) return nil @@ -55,6 +57,20 @@ func decryptedFileContentEqualToFileContent(ctx context.Context, filename string return nil } +func legacyCodeWithCipherKeyAndConstantVector(ctx context.Context, cipherKey string) error { + cryptoState := getCryptoState(ctx) + cryptoState.legacyCodeCipherKey = cipherKey + cryptoState.legacyCodeRandomIv = false + return nil +} + +func legacyCodeWithCipherKeyAndRandomVector(ctx context.Context, cipherKey string) error { + cryptoState := getCryptoState(ctx) + cryptoState.legacyCodeCipherKey = cipherKey + cryptoState.legacyCodeRandomIv = true + return nil +} + func encryptedFileSuccessfullyDecryptedByLegacyCodeWithCipherKeyAndVector(ctx context.Context, cipherKey string, iv string) error { riv := randomIv(iv) cryptoState := getCryptoState(ctx) @@ -87,7 +103,8 @@ func encryptedFileSuccessfullyDecryptedByLegacyCodeWithCipherKeyAndVector(ctx co func iDecryptFileAs(ctx context.Context, filename string, decryptionType string) error { cryptoState := getCryptoState(ctx) - module, e := cryptoState.createModule() + + module, e := cryptoState.getCryptoModule() if e != nil { return e } @@ -119,7 +136,7 @@ func iDecryptFile(ctx context.Context, filename string) error { cryptoState := getCryptoState(ctx) - module, e := cryptoState.createModule() + module, e := cryptoState.getCryptoModule() if e != nil { return e } @@ -132,7 +149,6 @@ func iDecryptFile(ctx context.Context, filename string) error { if e != nil { cryptoState.err = e } - _ = file.Close() return nil } @@ -148,7 +164,7 @@ func createCryptor(name string, cipherKey string, randomIv bool) (crypto.Cryptor func iEncryptFileAs(ctx context.Context, filename string, encryptionType string) error { cryptoState := getCryptoState(ctx) - module, e := cryptoState.createModule() + module, e := cryptoState.getCryptoModule() if e != nil { return e } @@ -172,7 +188,6 @@ func iEncryptFileAs(ctx context.Context, filename string, encryptionType string) } } - _ = file.Close() return nil } @@ -197,7 +212,7 @@ func iReceiveSuccess(ctx context.Context) error { return nil } -func iReceiveUnknownCryptorError(ctx context.Context) error { +func iReceiveUnknownCryptoError(ctx context.Context) error { cryptoState := getCryptoState(ctx) if cryptoState.err != nil { if strings.Contains(cryptoState.err.Error(), "unknown crypto error") { @@ -231,3 +246,27 @@ func withVector(ctx context.Context, iv string) error { cryptoState.randomIv = randomIv(iv) return nil } + +func successfullyDecryptAnEncryptedFileWithLegacyCode(ctx context.Context) error { + cryptoState := getCryptoState(ctx) + if cryptoState.result != nil { + _, e := utils.DecryptString(cryptoState.legacyCodeCipherKey, base64.StdEncoding.EncodeToString(cryptoState.result), cryptoState.legacyCodeRandomIv) + return e + } else if cryptoState.resultReader != nil { + buffer := &CloserBuffer{bytes.NewBuffer(nil)} + + utils.DecryptFile(cryptoState.legacyCodeCipherKey, 0, cryptoState.resultReader, buffer) + _, e := io.ReadAll(buffer) + return e + } else { + return errors.New("no result") + } +} + +type CloserBuffer struct { + *bytes.Buffer +} + +func (c *CloserBuffer) Close() error { + return nil +} diff --git a/tests/contract/steps_mapping_test.go b/tests/contract/steps_mapping_test.go index 39d6e0bd..45e3da68 100644 --- a/tests/contract/steps_mapping_test.go +++ b/tests/contract/steps_mapping_test.go @@ -56,8 +56,6 @@ func MapSteps(ctx *godog.ScenarioContext) { ctx.Step(`^the result is successful$`, theResultIsSuccessful) ctx.Step(`^the token string \'(.*)\'$`, theTokenString) - ctx.Step(`^\'(.*)\' crypto algorithm$`, cryptor) - ctx.Step(`^cryptor module with registered \'(.*)\' and \'(.*)\' crypto algorithms$`, cryptoModuleWithRegisteredcryptors) ctx.Step(`^Decrypted file content equal to the \'(.*)\' file content$`, decryptedFileContentEqualToFileContent) ctx.Step(`^Encrypted file successfully decrypted by legacy code with \'(.*)\' cipher key and \'(.*)\' vector$`, encryptedFileSuccessfullyDecryptedByLegacyCodeWithCipherKeyAndVector) ctx.Step(`^I decrypt \'(.*)\' file as \'(.*)\'$`, iDecryptFileAs) @@ -65,7 +63,14 @@ func MapSteps(ctx *godog.ScenarioContext) { ctx.Step(`^I encrypt \'(.*)\' file as \'(.*)\'$`, iEncryptFileAs) ctx.Step(`^I receive \'decryption error\'$`, iReceiveDecryptionError) ctx.Step(`^I receive \'success\'$`, iReceiveSuccess) - ctx.Step(`^I receive \'unknown cryptor error\'$`, iReceiveUnknownCryptorError) + ctx.Step(`^I receive \'unknown cryptor error\'$`, iReceiveUnknownCryptoError) ctx.Step(`^with \'(.*)\' cipher key$`, withCipherKey) ctx.Step(`^with \'(.*)\' vector$`, withVector) + + ctx.Step(`^Crypto module with \'(.*)\' cryptor$`, cryptor) + ctx.Step(`^Crypto module with default \'(.*)\' and additional \'(.*)\' cryptors$`, cryptoModuleWithDefaultAndAdditionalLegacyCryptors) + ctx.Step(`^Crypto module with \'(.*)\' cryptor$`, cryptor) + ctx.Step(`^Legacy code with \'(.*)\' cipher key and \'constant\' vector$`, legacyCodeWithCipherKeyAndConstantVector) + ctx.Step(`^Legacy code with \'(.*)\' cipher key and \'random\' vector$`, legacyCodeWithCipherKeyAndRandomVector) + ctx.Step(`^Successfully decrypt an encrypted file with legacy code$`, successfullyDecryptAnEncryptedFileWithLegacyCode) } From 2f6b5fc7e846f6a2bfb43e137d4ad92d0dadbd01 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Mon, 18 Sep 2023 16:24:17 +0200 Subject: [PATCH 12/30] Remove unneeded step --- tests/contract/crypto_steps_test.go | 35 +++------------------------- tests/contract/steps_mapping_test.go | 1 - 2 files changed, 3 insertions(+), 33 deletions(-) diff --git a/tests/contract/crypto_steps_test.go b/tests/contract/crypto_steps_test.go index 1b4cc788..cbc7b634 100644 --- a/tests/contract/crypto_steps_test.go +++ b/tests/contract/crypto_steps_test.go @@ -71,35 +71,6 @@ func legacyCodeWithCipherKeyAndRandomVector(ctx context.Context, cipherKey strin return nil } -func encryptedFileSuccessfullyDecryptedByLegacyCodeWithCipherKeyAndVector(ctx context.Context, cipherKey string, iv string) error { - riv := randomIv(iv) - cryptoState := getCryptoState(ctx) - - cryptor, e := createCryptor("legacy", cipherKey, riv) - if e != nil { - return e - } - module := crypto.NewCryptoModule(cryptor, []crypto.Cryptor{cryptor}) - if e != nil { - return e - } - if cryptoState.result != nil { - _, err := module.Decrypt(cryptoState.result) - if err != nil { - return err - } - } else if cryptoState.resultReader != nil { - _, err := module.DecryptStream(bufio.NewReader(cryptoState.resultReader)) - if err != nil { - return err - } - } else { - return errors.New("no result") - } - - return nil -} - func iDecryptFileAs(ctx context.Context, filename string, decryptionType string) error { cryptoState := getCryptoState(ctx) @@ -253,7 +224,7 @@ func successfullyDecryptAnEncryptedFileWithLegacyCode(ctx context.Context) error _, e := utils.DecryptString(cryptoState.legacyCodeCipherKey, base64.StdEncoding.EncodeToString(cryptoState.result), cryptoState.legacyCodeRandomIv) return e } else if cryptoState.resultReader != nil { - buffer := &CloserBuffer{bytes.NewBuffer(nil)} + buffer := &closerBuffer{bytes.NewBuffer(nil)} utils.DecryptFile(cryptoState.legacyCodeCipherKey, 0, cryptoState.resultReader, buffer) _, e := io.ReadAll(buffer) @@ -263,10 +234,10 @@ func successfullyDecryptAnEncryptedFileWithLegacyCode(ctx context.Context) error } } -type CloserBuffer struct { +type closerBuffer struct { *bytes.Buffer } -func (c *CloserBuffer) Close() error { +func (c *closerBuffer) Close() error { return nil } diff --git a/tests/contract/steps_mapping_test.go b/tests/contract/steps_mapping_test.go index 45e3da68..b5db113e 100644 --- a/tests/contract/steps_mapping_test.go +++ b/tests/contract/steps_mapping_test.go @@ -57,7 +57,6 @@ func MapSteps(ctx *godog.ScenarioContext) { ctx.Step(`^the token string \'(.*)\'$`, theTokenString) ctx.Step(`^Decrypted file content equal to the \'(.*)\' file content$`, decryptedFileContentEqualToFileContent) - ctx.Step(`^Encrypted file successfully decrypted by legacy code with \'(.*)\' cipher key and \'(.*)\' vector$`, encryptedFileSuccessfullyDecryptedByLegacyCodeWithCipherKeyAndVector) ctx.Step(`^I decrypt \'(.*)\' file as \'(.*)\'$`, iDecryptFileAs) ctx.Step(`^I decrypt \'(.*)\' file$`, iDecryptFile) ctx.Step(`^I encrypt \'(.*)\' file as \'(.*)\'$`, iEncryptFileAs) From 95f655fdd4ccb5c1032e4460e19ea7cc79650b09 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Mon, 18 Sep 2023 16:34:16 +0200 Subject: [PATCH 13/30] Hopefully now --- files_download_file.go | 4 +++- publish_request_test.go | 21 ++++++++++++--------- subscription_manager.go | 4 ++-- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/files_download_file.go b/files_download_file.go index 99913a29..f1cc58c7 100644 --- a/files_download_file.go +++ b/files_download_file.go @@ -94,7 +94,9 @@ func (b *downloadFileBuilder) Execute() (*PNDownloadFileResponse, StatusResponse var respDL *PNDownloadFileResponse if b.opts.CipherKey == "" && b.opts.pubnub.cryptoModule == nil { - + respDL = &PNDownloadFileResponse{ + File: resp.Body, + } } else { var e error cryptoModule := b.opts.pubnub.getCryptoModule() diff --git a/publish_request_test.go b/publish_request_test.go index 23989e6e..0bf42dfe 100644 --- a/publish_request_test.go +++ b/publish_request_test.go @@ -191,10 +191,10 @@ func AssertSuccessPublishGetMeta(t *testing.T, expectedString string, message in assert.Nil(err1) } -func AssertSuccessPublishPost(t *testing.T, expectedBody string, message interface{}) { +func AssertSuccessPublishPost(t *testing.T, pn *PubNub, expectedBody string, message interface{}) { assert := assert.New(t) - opts := newPublishOpts(pubnub, pubnub.ctx) + opts := newPublishOpts(pn, pn.ctx) opts.Channel = "ch" opts.Message = message opts.UsePost = true @@ -215,6 +215,7 @@ func AssertSuccessPublishPost(t *testing.T, expectedBody string, message interfa } func TestPublishMixedGet(t *testing.T) { + pn := NewPubNub(NewDemoConfig()) type msg struct { One string `json:"one"` Two string `json:"two"` @@ -270,6 +271,8 @@ func TestPublishMixedGet(t *testing.T) { } func TestPublishMixedPost(t *testing.T) { + pn := NewPubNub(NewDemoConfig()) + type msg struct { One string `json:"one"` Two string `json:"two"` @@ -282,16 +285,16 @@ func TestPublishMixedPost(t *testing.T) { msgMap["two"] = "hey2" msgMap["three"] = "hey3" - AssertSuccessPublishPost(t, "12", 12) - AssertSuccessPublishPost(t, "\"hey\"", "hey") - AssertSuccessPublishPost(t, "true", true) - AssertSuccessPublishPost(t, "[\"hey1\",\"hey2\",\"hey3\"]", + AssertSuccessPublishPost(t, pn, "12", 12) + AssertSuccessPublishPost(t, pn, "\"hey\"", "hey") + AssertSuccessPublishPost(t, pn, "true", true) + AssertSuccessPublishPost(t, pn, "[\"hey1\",\"hey2\",\"hey3\"]", []string{"hey1", "hey2", "hey3"}) - AssertSuccessPublishPost(t, "[1,2,3]", []int{1, 2, 3}) - AssertSuccessPublishPost(t, + AssertSuccessPublishPost(t, pn, "[1,2,3]", []int{1, 2, 3}) + AssertSuccessPublishPost(t, pn, "{\"one\":\"hey1\",\"two\":\"hey2\",\"three\":\"hey3\"}", msgStruct) - AssertSuccessPublishPost(t, + AssertSuccessPublishPost(t, pn, "{\"one\":\"hey1\",\"three\":\"hey3\",\"two\":\"hey2\"}", msgMap) } diff --git a/subscription_manager.go b/subscription_manager.go index 8d019587..1873f313 100644 --- a/subscription_manager.go +++ b/subscription_manager.go @@ -661,7 +661,7 @@ func processNonPresencePayload(m *SubscriptionManager, payload subscribeMessage, m.listenerManager.announceMessageActionsEvent(pnMessageActionsEvent) case PNMessageTypeFile: var err error - messagePayload, err = parseCipherInterface(payload.Payload, m.pubnub.Config, m.pubnub.cryptoModule) + messagePayload, err = parseCipherInterface(payload.Payload, m.pubnub.Config, m.pubnub.getCryptoModule()) if err != nil { pnStatus := &PNStatus{ Category: PNBadRequestCategory, @@ -680,7 +680,7 @@ func processNonPresencePayload(m *SubscriptionManager, payload subscribeMessage, m.listenerManager.announceFile(pnFilesEvent) default: var err error - messagePayload, err = parseCipherInterface(payload.Payload, m.pubnub.Config, m.pubnub.cryptoModule) + messagePayload, err = parseCipherInterface(payload.Payload, m.pubnub.Config, m.pubnub.getCryptoModule()) if err != nil { pnStatus := &PNStatus{ Category: PNBadRequestCategory, From abc12513f524cc3486075af89fdad89a4a680382 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Mon, 18 Sep 2023 16:37:24 +0200 Subject: [PATCH 14/30] Stupid stupid stupid --- publish_request_test.go | 1 - pubnub.go | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/publish_request_test.go b/publish_request_test.go index 0bf42dfe..8f237c6c 100644 --- a/publish_request_test.go +++ b/publish_request_test.go @@ -215,7 +215,6 @@ func AssertSuccessPublishPost(t *testing.T, pn *PubNub, expectedBody string, mes } func TestPublishMixedGet(t *testing.T) { - pn := NewPubNub(NewDemoConfig()) type msg struct { One string `json:"one"` Two string `json:"two"` diff --git a/pubnub.go b/pubnub.go index 18f15423..dfacba0c 100644 --- a/pubnub.go +++ b/pubnub.go @@ -80,6 +80,7 @@ type PubNub struct { previousIvFlag bool } +// TODO this needs to be tested func (pn *PubNub) getCryptoModule() crypto.CryptoModule { pn.Lock() defer pn.Unlock() From a1b0e73d8ed41318b955c811e8336b1c0cc51d88 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Mon, 18 Sep 2023 16:46:45 +0200 Subject: [PATCH 15/30] Demodemodemodemodemodemodemodemo --- publish_request_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/publish_request_test.go b/publish_request_test.go index 8f237c6c..b14e2e0d 100644 --- a/publish_request_test.go +++ b/publish_request_test.go @@ -206,7 +206,7 @@ func AssertSuccessPublishPost(t *testing.T, pn *PubNub, expectedBody string, mes Path: path, } h.AssertPathsEqual(t, - "/publish/pub_key/sub_key/0/ch/0", + "/publish/demo/demo/0/ch/0", u.EscapedPath(), []int{}) body, err := opts.buildBody() From 0a4e162b0adcf27ce3e6bae23a1bf558d80864a2 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Mon, 18 Sep 2023 17:16:12 +0200 Subject: [PATCH 16/30] Fixes --- crypto/aes_cbc_cryptor_test.go | 10 +- crypto/decrypting_reader_test.go | 3 +- crypto/encrypting_reader_test.go | 3 +- crypto/legacy_cryptor_test.go | 7 +- tests/e2e/file_upload_dec_output.txt | 352 --------------------- tests/e2e/file_upload_sample_encrypted.txt | Bin 21904 -> 0 bytes tests/e2e/files_test.go | 34 -- 7 files changed, 10 insertions(+), 399 deletions(-) delete mode 100644 tests/e2e/file_upload_dec_output.txt delete mode 100644 tests/e2e/file_upload_sample_encrypted.txt diff --git a/crypto/aes_cbc_cryptor_test.go b/crypto/aes_cbc_cryptor_test.go index a6b47b15..ea17d909 100644 --- a/crypto/aes_cbc_cryptor_test.go +++ b/crypto/aes_cbc_cryptor_test.go @@ -7,6 +7,10 @@ import ( "testing/quick" ) +var defaultPropertyTestConfig = &quick.Config{ + MaxCount: 1000, +} + func canDecryptEncryptStreamResult(in []byte) bool { cryptor, e := NewAesCbcCryptor("enigma") if e != nil { @@ -61,15 +65,13 @@ func canDecryptStreamEncryptResult(in []byte) bool { } func Test_AesCBC_EncryptStream(t *testing.T) { - c := quick.Config{MaxCount: 10000} - if err := quick.Check(canDecryptEncryptStreamResult, &c); err != nil { + if err := quick.Check(canDecryptEncryptStreamResult, defaultPropertyTestConfig); err != nil { t.Error(err) } } func Test_AesCBC_DecryptStream(t *testing.T) { - c := quick.Config{MaxCount: 10000} - if err := quick.Check(canDecryptStreamEncryptResult, &c); err != nil { + if err := quick.Check(canDecryptStreamEncryptResult, defaultPropertyTestConfig); err != nil { t.Error(err) } } diff --git a/crypto/decrypting_reader_test.go b/crypto/decrypting_reader_test.go index 949d3fe9..2c2f50ad 100644 --- a/crypto/decrypting_reader_test.go +++ b/crypto/decrypting_reader_test.go @@ -42,8 +42,7 @@ func canReadDifferentSizeOfChunks(in []byte, bufferSize uint8) bool { } func Test_DecryptingReader_ReadDifferentSizeOfBuffers(t *testing.T) { - c := quick.Config{MaxCount: 10000} - if err := quick.Check(canReadDifferentSizeOfChunks, &c); err != nil { + if err := quick.Check(canReadDifferentSizeOfChunks, defaultPropertyTestConfig); err != nil { t.Error(err) } } diff --git a/crypto/encrypting_reader_test.go b/crypto/encrypting_reader_test.go index fd76a057..109f2582 100644 --- a/crypto/encrypting_reader_test.go +++ b/crypto/encrypting_reader_test.go @@ -31,8 +31,7 @@ func encryptingReaderCanReadDifferentSizeOfChunks(in []byte, bufferSize uint8) b } func Test_EncryptingReader_ReadDifferentSizeOfBuffers(t *testing.T) { - c := quick.Config{MaxCount: 10000} - if err := quick.Check(encryptingReaderCanReadDifferentSizeOfChunks, &c); err != nil { + if err := quick.Check(encryptingReaderCanReadDifferentSizeOfChunks, defaultPropertyTestConfig); err != nil { t.Error(err) } } diff --git a/crypto/legacy_cryptor_test.go b/crypto/legacy_cryptor_test.go index 0db1f83b..f2fc0fd0 100644 --- a/crypto/legacy_cryptor_test.go +++ b/crypto/legacy_cryptor_test.go @@ -60,16 +60,13 @@ func legacyCanDecryptStreamEncryptResult(in []byte) bool { } func Test_Legacy_EncryptStream(t *testing.T) { - c := quick.Config{MaxCount: 10000} - - if err := quick.Check(legacyCanDecryptEncryptStreamResult, &c); err != nil { + if err := quick.Check(legacyCanDecryptEncryptStreamResult, defaultPropertyTestConfig); err != nil { t.Error(err) } } func Test_Legacy_DecryptStream(t *testing.T) { - c := quick.Config{MaxCount: 10000} - if err := quick.Check(legacyCanDecryptStreamEncryptResult, &c); err != nil { + if err := quick.Check(legacyCanDecryptStreamEncryptResult, defaultPropertyTestConfig); err != nil { t.Error(err) } } diff --git a/tests/e2e/file_upload_dec_output.txt b/tests/e2e/file_upload_dec_output.txt deleted file mode 100644 index 226f4a06..00000000 --- a/tests/e2e/file_upload_dec_output.txt +++ /dev/null @@ -1,352 +0,0 @@ -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== -File upload test - -Junk text start -============================== - - -Oh to talking improve produce in limited offices fifteen an. Wicket branch to answer do we. Place are decay men hours tiled. If or of ye throwing friendly required. Marianne interest in exertion as. Offering my branched confined oh dashwood. - -Their could can widen ten she any. As so we smart those money in. Am wrote up whole so tears sense oh. Absolute required of reserved in offering no. How sense found our those gay again taken the. Improved property reserved disposal do offering me. - -Same an quit most an. Admitting an mr disposing sportsmen. Tried on cause no spoil arise plate. Longer valley get esteem use led six. Middletons resolution advantages expression themselves partiality so me at. West none hope if sing oh sent tell is. - -============================== -============================== - -END \ No newline at end of file diff --git a/tests/e2e/file_upload_sample_encrypted.txt b/tests/e2e/file_upload_sample_encrypted.txt deleted file mode 100644 index 6e61f95832057ab82f2a6a32b2455d776b5df647..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21904 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 Date: Mon, 18 Sep 2023 17:24:43 +0200 Subject: [PATCH 17/30] Make e2e tests easier to run --- go.mod | 1 + go.sum | 2 ++ tests/e2e/file_upload_test_output.txt | Bin 21904 -> 21879 bytes tests/e2e/helper.go | 3 +++ 4 files changed, 6 insertions(+) diff --git a/go.mod b/go.mod index 38a328ec..00b3da7f 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/brianolson/cbor_go v1.0.0 github.com/cucumber/godog v0.12.0 github.com/google/uuid v1.3.0 + github.com/joho/godotenv v1.5.1 // indirect github.com/stretchr/testify v1.7.0 golang.org/x/net v0.8.0 golang.org/x/text v0.8.0 // indirect diff --git a/go.sum b/go.sum index e5b99115..242284dd 100644 --- a/go.sum +++ b/go.sum @@ -112,6 +112,8 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= diff --git a/tests/e2e/file_upload_test_output.txt b/tests/e2e/file_upload_test_output.txt index 6e61f95832057ab82f2a6a32b2455d776b5df647..226f4a064a1e4eced5be9835d0c6c131b4aa3d6a 100644 GIT binary patch literal 21879 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 Date: Wed, 20 Sep 2023 17:27:02 +0200 Subject: [PATCH 18/30] Apply suggestions from code review Co-authored-by: Mateusz Dahlke <39696234+Xavrax@users.noreply.github.com> --- config.go | 2 +- crypto/encrypting_reader.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config.go b/config.go index d0bd6b69..020cb25c 100644 --- a/config.go +++ b/config.go @@ -51,7 +51,7 @@ type Config struct { StoreTokensOnGrant bool // Will store grant v3 tokens in token manager for further use. FileMessagePublishRetryLimit int // The number of tries made in case of Publish File Message failure. UseRandomInitializationVector bool // When true the IV will be random for all requests and not just file upload. When false the IV will be hardcoded for all requests except File Upload - CryptoModule crypto.CryptoModule //a cryptography module used for encryption and decryption + CryptoModule crypto.CryptoModule // A cryptography module used for encryption and decryption } // NewDemoConfig initiates the config with demo keys, for tests only. diff --git a/crypto/encrypting_reader.go b/crypto/encrypting_reader.go index f8530825..f113cdfb 100644 --- a/crypto/encrypting_reader.go +++ b/crypto/encrypting_reader.go @@ -70,7 +70,7 @@ func (encryptingReader *blockModeEncryptingReader) Read(p []byte) (n int, err er return 0, errors.New("cannot read into empty buffer") } - if (encryptingReader.err != nil) && encryptingReader.buffer.Len() == 0 { + if encryptingReader.err != nil && encryptingReader.buffer.Len() == 0 { return 0, encryptingReader.err } From 9cbfda00ca02139a40232d7e441b7b438c89361f Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Wed, 20 Sep 2023 17:31:00 +0200 Subject: [PATCH 19/30] After review changes --- crypto/cryptor_header.go | 4 ++-- crypto/decrypting_reader.go | 2 +- crypto/decrypting_reader_test.go | 5 +++++ crypto/encrypting_reader_test.go | 5 +++++ crypto/module_test.go | 22 ++++++++++++++++++++++ fire_request.go | 6 +++--- 6 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 crypto/module_test.go diff --git a/crypto/cryptor_header.go b/crypto/cryptor_header.go index b6dd3bb8..458342cf 100644 --- a/crypto/cryptor_header.go +++ b/crypto/cryptor_header.go @@ -25,7 +25,7 @@ func headerV1(cryptorId string, metadata []byte) ([]byte, error) { cryptorDataSize := len(metadata) var cryptorDataBytesSize int - if cryptorDataSize <= maxShortSize { + if cryptorDataSize < longSizeIndicator { cryptorDataBytesSize = shortSizeLength } else { cryptorDataBytesSize = longSizeLength @@ -56,7 +56,7 @@ func headerV1(cryptorId string, metadata []byte) ([]byte, error) { if e != nil { return nil, e } - _, e = buffer.Write([]byte(strconv.FormatInt(int64(cryptorDataSize), 10))) + _, e = buffer.Write([]byte(strconv.FormatInt(int64(cryptorDataSize), 10))) //TODO if e != nil { return nil, e } diff --git a/crypto/decrypting_reader.go b/crypto/decrypting_reader.go index 2624beb1..95bc71df 100644 --- a/crypto/decrypting_reader.go +++ b/crypto/decrypting_reader.go @@ -30,7 +30,7 @@ func (decryptingReader *blockModeDecryptingReader) readNextBlock() ([]byte, erro output := make([]byte, decryptingReader.blockMode.BlockSize()) sizeOfCurrentlyRead, readErr := io.ReadFull(reader, output) if readErr != nil && readErr != io.EOF { - return output[:sizeOfCurrentlyRead], readErr + return nil, readErr } if sizeOfCurrentlyRead == 0 && readErr == io.EOF { diff --git a/crypto/decrypting_reader_test.go b/crypto/decrypting_reader_test.go index 2c2f50ad..0d5018bf 100644 --- a/crypto/decrypting_reader_test.go +++ b/crypto/decrypting_reader_test.go @@ -2,6 +2,7 @@ package crypto import ( "bytes" + "io" "testing" "testing/quick" ) @@ -36,6 +37,10 @@ func canReadDifferentSizeOfChunks(in []byte, bufferSize uint8) bool { readDataBuffer.Write(buffer[:readBytes]) } + if e != nil && e != io.EOF { + return false + } + out := readDataBuffer.Bytes()[:numberOfReadBytes] return bytes.Equal(in, out) diff --git a/crypto/encrypting_reader_test.go b/crypto/encrypting_reader_test.go index 109f2582..45dfb3e3 100644 --- a/crypto/encrypting_reader_test.go +++ b/crypto/encrypting_reader_test.go @@ -2,6 +2,7 @@ package crypto import ( "bytes" + "io" "testing" "testing/quick" ) @@ -25,6 +26,10 @@ func encryptingReaderCanReadDifferentSizeOfChunks(in []byte, bufferSize uint8) b readDataBuffer.Write(buffer[:readBytes]) } + if e != nil && e != io.EOF { + return false + } + out := readDataBuffer.Bytes()[:numberOfReadBytes] return bytes.Equal(inPadded, out) diff --git a/crypto/module_test.go b/crypto/module_test.go new file mode 100644 index 00000000..8f402e3e --- /dev/null +++ b/crypto/module_test.go @@ -0,0 +1,22 @@ +package crypto + +import ( + "encoding/base64" + "testing" +) + +func TestToProduceData(t *testing.T) { + cipherKey := "myCipherKey" + textToEncrypt := "Hello world encrypted with " + + legacyModuleStaticIv, _ := NewLegacyCryptoModule(cipherKey, false) + legacyModuleRandomIv, _ := NewLegacyCryptoModule(cipherKey, true) + aesCbcModule, _ := NewAesCbcCryptoModule(cipherKey, true) + r1, _ := legacyModuleStaticIv.Encrypt([]byte(textToEncrypt + "legacyModuleStaticIv")) + r2, _ := legacyModuleRandomIv.Encrypt([]byte(textToEncrypt + "legacyModuleRandomIv")) + r3, _ := aesCbcModule.Encrypt([]byte(textToEncrypt + "aesCbcModule")) + + println(base64.StdEncoding.EncodeToString(r1)) + println(base64.StdEncoding.EncodeToString(r2)) + println(base64.StdEncoding.EncodeToString(r3)) +} diff --git a/fire_request.go b/fire_request.go index 51b13e48..e5a631e1 100644 --- a/fire_request.go +++ b/fire_request.go @@ -215,7 +215,7 @@ func (o *fireOpts) buildBody() ([]byte, error) { if err != nil { return []byte{}, err } - msg = []byte(m) + msg = m } else { if s, ok := o.Message.(string); ok { msg = []byte(s) @@ -230,11 +230,11 @@ func (o *fireOpts) buildBody() ([]byte, error) { if err != nil { return []byte{}, err } - msg, err := utils.ValueAsString(enc) //TODO WHAT?! + m, err := utils.ValueAsString(enc) if err != nil { return []byte{}, err } - return []byte(msg), nil + return m, nil } return msg, nil } From 4ef0fec535f1038f60da8efb4ca200048ff7ba20 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Thu, 21 Sep 2023 16:36:16 +0200 Subject: [PATCH 20/30] After review changes. Next part --- config.go | 3 ++- files_download_file.go | 2 +- pubnub.go | 22 +++++++--------------- pubnub_test.go | 27 +++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/config.go b/config.go index 020cb25c..6f68fcef 100644 --- a/config.go +++ b/config.go @@ -27,7 +27,8 @@ type Config struct { // UUID to be used as a device identifier. // //Deprecated: please use SetUserId/GetUserId - UUID string + UUID string + //DEPRECATED: please use CryptoModule CipherKey string // If CipherKey is passed, all communications to/from PubNub will be encrypted. Secure bool // True to use TLS ConnectTimeout int // net.Dialer.Timeout diff --git a/files_download_file.go b/files_download_file.go index f1cc58c7..260dab2f 100644 --- a/files_download_file.go +++ b/files_download_file.go @@ -93,7 +93,7 @@ func (b *downloadFileBuilder) Execute() (*PNDownloadFileResponse, StatusResponse } var respDL *PNDownloadFileResponse - if b.opts.CipherKey == "" && b.opts.pubnub.cryptoModule == nil { + if b.opts.CipherKey == "" && b.opts.pubnub.getCryptoModule() == nil { respDL = &PNDownloadFileResponse{ File: resp.Body, } diff --git a/pubnub.go b/pubnub.go index dfacba0c..7e07cd5c 100644 --- a/pubnub.go +++ b/pubnub.go @@ -75,7 +75,6 @@ type PubNub struct { ctx Context cancel func() tokenManager *TokenManager - cryptoModule crypto.CryptoModule previousCipherKey string previousIvFlag bool } @@ -85,15 +84,15 @@ func (pn *PubNub) getCryptoModule() crypto.CryptoModule { pn.Lock() defer pn.Unlock() if pn.previousCipherKey == pn.Config.CipherKey && pn.previousIvFlag == pn.Config.UseRandomInitializationVector { - return pn.cryptoModule + return pn.Config.CryptoModule } if pn.Config != nil && pn.Config.CipherKey != "" { - pn.cryptoModule, _ = crypto.NewLegacyCryptoModule(pn.Config.CipherKey, pn.Config.UseRandomInitializationVector) - return pn.cryptoModule + pn.Config.CryptoModule, _ = crypto.NewLegacyCryptoModule(pn.Config.CipherKey, pn.Config.UseRandomInitializationVector) + return pn.Config.CryptoModule } else if pn.Config != nil && pn.Config.CipherKey == "" { - pn.cryptoModule = nil - return pn.cryptoModule + pn.Config.CryptoModule = nil + return pn.Config.CryptoModule } return nil } @@ -782,6 +781,8 @@ func NewPubNub(pnconf *Config) *PubNub { nextPublishSequence: 0, ctx: ctx, cancel: cancel, + previousIvFlag: pnconf.UseRandomInitializationVector, + previousCipherKey: pnconf.CipherKey, } pn.subscriptionManager = newSubscriptionManager(pn, ctx) @@ -790,15 +791,6 @@ func NewPubNub(pnconf *Config) *PubNub { pn.jobQueue = make(chan *JobQItem) pn.requestWorkers = pn.newNonSubQueueProcessor(pnconf.MaxWorkers, ctx) pn.tokenManager = newTokenManager(pn, ctx) - if pnconf.CryptoModule != nil { - pn.cryptoModule = pnconf.CryptoModule - } else if pnconf.CipherKey != "" { - module, err := crypto.NewLegacyCryptoModule(pnconf.CipherKey, pnconf.UseRandomInitializationVector) - if err != nil { - panic(err) - } - pn.cryptoModule = module - } return pn } diff --git a/pubnub_test.go b/pubnub_test.go index 6c52214d..948b0615 100644 --- a/pubnub_test.go +++ b/pubnub_test.go @@ -1,6 +1,7 @@ package pubnub import ( + "github.com/pubnub/go/v7/crypto" "testing" "github.com/stretchr/testify/assert" @@ -20,6 +21,32 @@ func TestInitializer(t *testing.T) { assert.Equal("my_secret_key", pubnub.Config.SecretKey) } +func TestCryptoModuleChangesWhenCipherKeyChanges(t *testing.T) { + pubnub := NewPubNubDemo() + + cryptoModule, _ := crypto.NewAesCbcCryptoModule("my_cipher_key", true) + pubnub.Config.CryptoModule = cryptoModule + a := assert.New(t) + + a.Equal(cryptoModule, pubnub.getCryptoModule()) + + pubnub.Config.CipherKey = "new_cipher_key" + a.NotEqual(cryptoModule, pubnub.getCryptoModule()) +} + +func TestCryptoModuleChangesIfIvFlagChanges(t *testing.T) { + pubnub := NewPubNubDemo() + + cryptoModule, _ := crypto.NewAesCbcCryptoModule("my_cipher_key", true) + pubnub.Config.CryptoModule = cryptoModule + a := assert.New(t) + + a.Equal(cryptoModule, pubnub.getCryptoModule()) + + pubnub.Config.UseRandomInitializationVector = false + a.NotEqual(cryptoModule, pubnub.getCryptoModule()) +} + func TestDemoInitializer(t *testing.T) { demo := NewPubNubDemo() From 969ddef859d92e7a9118d6e672da921123c8d489 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Thu, 21 Sep 2023 18:25:47 +0200 Subject: [PATCH 21/30] Fix history_request_test.go file Tests in this file have been order dependent. I broke those dependencies by introducing few additional lines of code in each an every one. And the initHistoryOpts now requires passing pn, so maybe whoever decide to write new tests will realize that it might be nice to create new pubnub and new config instead of reusing them across every test. --- config.go | 45 +++++++++++++------------ crypto/module.go | 2 +- history_request_test.go | 74 +++++++++++++++++++++++++++-------------- pubnub.go | 7 ++++ 4 files changed, 80 insertions(+), 48 deletions(-) diff --git a/config.go b/config.go index 6f68fcef..42aaca95 100644 --- a/config.go +++ b/config.go @@ -29,28 +29,29 @@ type Config struct { //Deprecated: please use SetUserId/GetUserId UUID string //DEPRECATED: please use CryptoModule - CipherKey string // If CipherKey is passed, all communications to/from PubNub will be encrypted. - Secure bool // True to use TLS - ConnectTimeout int // net.Dialer.Timeout - NonSubscribeRequestTimeout int // http.Client.Timeout for non-subscribe requests - SubscribeRequestTimeout int // http.Client.Timeout for subscribe requests only - FileUploadRequestTimeout int // http.Client.Timeout File Upload Request only - HeartbeatInterval int // The frequency of the pings to the server to state that the client is active - PresenceTimeout int // The time after which the server will send a timeout for the client - MaximumReconnectionRetries int // The config sets how many times to retry to reconnect before giving up. - MaximumLatencyDataAge int // Max time to store the latency data for telemetry - FilterExpression string // Feature to subscribe with a custom filter expression. - PNReconnectionPolicy ReconnectionPolicy // Reconnection policy selection - Log *log.Logger // Logger instance - SuppressLeaveEvents bool // When true the SDK doesn't send out the leave requests. - DisablePNOtherProcessing bool // PNOther processing looks for pn_other in the JSON on the recevied message - UseHTTP2 bool // HTTP2 Flag - MessageQueueOverflowCount int // When the limit is exceeded by the number of messages received in a single subscribe request, a status event PNRequestMessageCountExceededCategory is fired. - MaxIdleConnsPerHost int // Used to set the value of HTTP Transport's MaxIdleConnsPerHost. - MaxWorkers int // Number of max workers for Publish and Grant requests - UsePAMV3 bool // Use PAM version 2, Objects requets would still use PAM v3 - StoreTokensOnGrant bool // Will store grant v3 tokens in token manager for further use. - FileMessagePublishRetryLimit int // The number of tries made in case of Publish File Message failure. + CipherKey string // If CipherKey is passed, all communications to/from PubNub will be encrypted. + Secure bool // True to use TLS + ConnectTimeout int // net.Dialer.Timeout + NonSubscribeRequestTimeout int // http.Client.Timeout for non-subscribe requests + SubscribeRequestTimeout int // http.Client.Timeout for subscribe requests only + FileUploadRequestTimeout int // http.Client.Timeout File Upload Request only + HeartbeatInterval int // The frequency of the pings to the server to state that the client is active + PresenceTimeout int // The time after which the server will send a timeout for the client + MaximumReconnectionRetries int // The config sets how many times to retry to reconnect before giving up. + MaximumLatencyDataAge int // Max time to store the latency data for telemetry + FilterExpression string // Feature to subscribe with a custom filter expression. + PNReconnectionPolicy ReconnectionPolicy // Reconnection policy selection + Log *log.Logger // Logger instance + SuppressLeaveEvents bool // When true the SDK doesn't send out the leave requests. + DisablePNOtherProcessing bool // PNOther processing looks for pn_other in the JSON on the recevied message + UseHTTP2 bool // HTTP2 Flag + MessageQueueOverflowCount int // When the limit is exceeded by the number of messages received in a single subscribe request, a status event PNRequestMessageCountExceededCategory is fired. + MaxIdleConnsPerHost int // Used to set the value of HTTP Transport's MaxIdleConnsPerHost. + MaxWorkers int // Number of max workers for Publish and Grant requests + UsePAMV3 bool // Use PAM version 2, Objects requets would still use PAM v3 + StoreTokensOnGrant bool // Will store grant v3 tokens in token manager for further use. + FileMessagePublishRetryLimit int // The number of tries made in case of Publish File Message failure. + //DEPRECATED: please use UseRandomInitializationVector UseRandomInitializationVector bool // When true the IV will be random for all requests and not just file upload. When false the IV will be hardcoded for all requests except File Upload CryptoModule crypto.CryptoModule // A cryptography module used for encryption and decryption } diff --git a/crypto/module.go b/crypto/module.go index 3cd7922a..d212a2cc 100644 --- a/crypto/module.go +++ b/crypto/module.go @@ -94,7 +94,7 @@ func (m *module) Decrypt(data []byte) ([]byte, error) { return nil, fmt.Errorf("unknown crypto error: unknown cryptor id %s", *id) } - return m.decryptors[*id].Decrypt(encryptedData) + return decryptor.Decrypt(encryptedData) } func (m *module) EncryptStream(input io.Reader) (io.Reader, error) { diff --git a/history_request_test.go b/history_request_test.go index 5ac1dbe1..bd049bec 100644 --- a/history_request_test.go +++ b/history_request_test.go @@ -15,7 +15,7 @@ var ( fakeResponseState = StatusResponse{} ) -func initHistoryOpts() *historyOpts { +func (pn *PubNub) initHistoryOpts() *historyOpts { opts := newHistoryOpts(pubnub, pubnub.ctx) opts.Channel = "ch" opts.Start = int64(100000) @@ -25,6 +25,7 @@ func initHistoryOpts() *historyOpts { opts.Reverse = false opts.Count = 3 opts.IncludeTimetoken = true + opts.pubnub = pn return opts } @@ -201,7 +202,9 @@ func TestHistoryResponseParsingStringMessages(t *testing.T) { jsonString := []byte(`[["hey-1","hey-two","hey-1","hey-1","hey-1","hey0","hey1","hey2","hey3","hey4","hey5","hey6","hey7","hey8","hey9","hey10","hey0","hey1","hey2","hey3","hey4","hey5","hey6","hey7","hey8","hey9","hey10","hey0","hey1","hey2","hey3","hey4","hey5","hey6","hey7","hey8","hey9","hey10"],14991775432719844,14991868111600528]`) - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + pn := NewPubNubDemo() + + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Nil(err) assert.Equal(int64(14991775432719844), resp.StartTimetoken) @@ -216,8 +219,9 @@ func TestHistoryResponseParsingStringWithTimetoken(t *testing.T) { assert := assert.New(t) jsonString := []byte(`[[{"timetoken":15232761410327866,"message":"hey-1"},{"timetoken":15232761410327866,"message":"hey-2"},{"timetoken":15232761410327866,"message":"hey-3"}],15232761410327866,15232761410327866]`) + pn := NewPubNubDemo() - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Nil(err) assert.Equal(int64(15232761410327866), resp.StartTimetoken) @@ -236,8 +240,9 @@ func TestHistoryResponseParsingInt(t *testing.T) { assert := assert.New(t) jsonString := []byte(`[[1,2,3,4,5,6,7],14991775432719844,14991868111600528]`) + pn := NewPubNubDemo() - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Nil(err) assert.Equal(int64(14991775432719844), resp.StartTimetoken) @@ -252,7 +257,8 @@ func TestHistoryResponseParsingInt1(t *testing.T) { int1 := int(1) jsonString := []byte(fmt.Sprintf("[[%d],14991775432719844,14991868111600528]", int1)) - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + pn := NewPubNubDemo() + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Nil(err) assert.Equal(int64(14991775432719844), resp.StartTimetoken) @@ -266,8 +272,9 @@ func TestHistoryResponseParsingSlice(t *testing.T) { assert := assert.New(t) jsonString := []byte(`[[[1,2,3],["one","two","three"]],14991775432719844,14991868111600528]`) + pn := NewPubNubDemo() - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Nil(err) assert.Equal(int64(14991775432719844), resp.StartTimetoken) @@ -275,10 +282,11 @@ func TestHistoryResponseParsingSlice(t *testing.T) { func TestHistoryResponseParsingMap(t *testing.T) { assert := assert.New(t) - pnconfig.CipherKey = "" + jsonString := []byte(`[[{"one":1,"two":2},{"three":3,"four":4}],14991775432719844,14991868111600528]`) + pn := NewPubNubDemo() - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Nil(err) assert.Equal(int64(14991775432719844), resp.StartTimetoken) @@ -291,13 +299,14 @@ func TestHistoryResponseParsingMap(t *testing.T) { func TestHistoryPNOther(t *testing.T) { assert := assert.New(t) - pnconfig.CipherKey = "testCipher" - pnconfig.UseRandomInitializationVector = false + pn := NewPubNubDemo() + pn.Config.CipherKey = "testCipher" + pn.Config.UseRandomInitializationVector = false int64Val := int64(14991775432719844) jsonString := []byte(`[[{"pn_other":"ven1bo79fk88nq5EIcnw/N9RmGzLeeWMnsabr1UL3iw="},1,"a",1.1,false,14991775432719844],14991775432719844,14991868111600528]`) - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Nil(err) assert.Equal(int64(14991775432719844), resp.StartTimetoken) @@ -331,11 +340,13 @@ func TestHistoryPNOther(t *testing.T) { func TestHistoryPNOtherYay(t *testing.T) { assert := assert.New(t) - pnconfig.CipherKey = "enigma" + pn := NewPubNubDemo() + pn.Config.UseRandomInitializationVector = false + pn.Config.CipherKey = "enigma" int64Val := int64(14991775432719844) jsonString := []byte(`[[{"pn_other":"Wi24KS4pcTzvyuGOHubiXg=="},1,"a",1.1,false,14991775432719844],14991775432719844,14991868111600528]`) - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Nil(err) assert.Equal(int64(14991775432719844), resp.StartTimetoken) @@ -369,7 +380,8 @@ func TestHistoryResponseParsingSliceInMapWithTimetoken(t *testing.T) { jsonString := []byte(`[[{"message":[1,2,3,["one","two","three"]],"timetoken":1111}],14991775432719844,14991868111600528]`) - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + pn := NewPubNubDemo() + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Nil(err) assert.Equal(int64(14991775432719844), resp.StartTimetoken) @@ -385,7 +397,8 @@ func TestHistoryResponseParsingMapInSlice(t *testing.T) { jsonString := []byte(`[[[{"one":"two","three":[5,6]}]],14991775432719844,14991868111600528]`) - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + pn := NewPubNubDemo() + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Nil(err) assert.Equal(int64(14991775432719844), resp.StartTimetoken) @@ -400,12 +413,14 @@ func TestHistoryResponseParsingMapInSlice(t *testing.T) { func TestHistoryEncrypt(t *testing.T) { assert := assert.New(t) + pnconfig := NewDemoConfig() pnconfig.CipherKey = "testCipher" + pnconfig.UseRandomInitializationVector = false pubnub = NewPubNub(pnconfig) jsonString := []byte(`[["MnwzPGdVgz2osQCIQJviGg=="],14991775432719844,14991868111600528]`) - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + resp, _, err := newHistoryResponse(jsonString, pubnub.initHistoryOpts(), fakeResponseState) assert.Nil(err) messages := resp.Messages @@ -415,11 +430,13 @@ func TestHistoryEncrypt(t *testing.T) { func TestHistoryEncryptSlice(t *testing.T) { assert := assert.New(t) - pnconfig.CipherKey = "testCipher" + pn := NewPubNubDemo() + pn.Config.CipherKey = "testCipher" + pn.Config.UseRandomInitializationVector = false jsonString := []byte(`[["gwkdY8qcv60GM/PslArWQsdXrQ6LwJD2HoaEfy0CjMc="],14991775432719844,14991868111600528]`) - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Nil(err) messages := resp.Messages @@ -436,11 +453,13 @@ func TestHistoryEncryptSlice(t *testing.T) { func TestHistoryEncryptMap(t *testing.T) { assert := assert.New(t) - pnconfig.CipherKey = "testCipher" + pn := NewPubNubDemo() + pn.Config.CipherKey = "testCipher" + pn.Config.UseRandomInitializationVector = false jsonString := []byte(`[["wIC13nvJcI4vBtWNFVUu0YDiqREr9kavB88xeyWTweDS363Yl84RCWqOHWTol4aY"],14991775432719844,14991868111600528]`) - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Nil(err) messages := resp.Messages @@ -464,7 +483,8 @@ func TestHistoryResponseMeta(t *testing.T) { jsonString := []byte(`[[{"message":"my-message","meta":{"m1":"n1","m2":"n2"}}],15699986472636251,15699986472636251]`) - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + pn := NewPubNubDemo() + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Nil(err) assert.Equal(int64(15699986472636251), resp.StartTimetoken) @@ -482,7 +502,8 @@ func TestHistoryResponseMetaAndTT(t *testing.T) { jsonString := []byte(`[[{"message":"my-message","meta":{"m1":"n1","m2":"n2"},"timetoken":15699986472636251}],15699986472636251,15699986472636251]`) - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + pn := NewPubNubDemo() + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Nil(err) assert.Equal(int64(15699986472636251), resp.StartTimetoken) @@ -501,7 +522,8 @@ func TestHistoryResponseError(t *testing.T) { jsonString := []byte(`s`) - _, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + pn := NewPubNubDemo() + _, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) } @@ -510,7 +532,8 @@ func TestHistoryResponseStartTTError(t *testing.T) { jsonString := []byte(`[[{"message":[1,2,3,["one","two","three"]],"timetoken":1111}],"s","a"]`) - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + pn := NewPubNubDemo() + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Equal(int64(0), resp.StartTimetoken) assert.Equal(int64(0), resp.EndTimetoken) assert.Nil(err) @@ -522,7 +545,8 @@ func TestHistoryResponseEndTTError(t *testing.T) { jsonString := []byte(`[[{"message":[1,2,3,["one","two","three"]],"timetoken":1111}],121324,"a"]`) - resp, _, err := newHistoryResponse(jsonString, initHistoryOpts(), fakeResponseState) + pn := NewPubNubDemo() + resp, _, err := newHistoryResponse(jsonString, pn.initHistoryOpts(), fakeResponseState) assert.Equal(int64(121324), resp.StartTimetoken) assert.Equal(int64(0), resp.EndTimetoken) assert.Nil(err) diff --git a/pubnub.go b/pubnub.go index 7e07cd5c..43216b95 100644 --- a/pubnub.go +++ b/pubnub.go @@ -785,6 +785,13 @@ func NewPubNub(pnconf *Config) *PubNub { previousCipherKey: pnconf.CipherKey, } + if pnconf.CipherKey != "" { + var e error + pn.Config.CryptoModule, e = crypto.NewLegacyCryptoModule(pnconf.CipherKey, pnconf.UseRandomInitializationVector) + if e != nil { + panic(e) + } + } pn.subscriptionManager = newSubscriptionManager(pn, ctx) pn.heartbeatManager = newHeartbeatManager(pn, ctx) pn.telemetryManager = newTelemetryManager(pnconf.MaximumLatencyDataAge, ctx) From dcb5113f432ff53a12fd539784a45808c2ba9890 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Fri, 22 Sep 2023 10:10:26 +0200 Subject: [PATCH 22/30] Fix it better ;) --- history_request_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/history_request_test.go b/history_request_test.go index bd049bec..8eb31d6b 100644 --- a/history_request_test.go +++ b/history_request_test.go @@ -416,7 +416,7 @@ func TestHistoryEncrypt(t *testing.T) { pnconfig := NewDemoConfig() pnconfig.CipherKey = "testCipher" pnconfig.UseRandomInitializationVector = false - pubnub = NewPubNub(pnconfig) + pubnub := NewPubNub(pnconfig) jsonString := []byte(`[["MnwzPGdVgz2osQCIQJviGg=="],14991775432719844,14991868111600528]`) From 1d0e98931442d4da6553e509ac9cf61ba596ed41 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Fri, 22 Sep 2023 12:13:50 +0200 Subject: [PATCH 23/30] Maybe? --- tests/e2e/publish_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/e2e/publish_test.go b/tests/e2e/publish_test.go index 52a5f62b..527202be 100644 --- a/tests/e2e/publish_test.go +++ b/tests/e2e/publish_test.go @@ -2,6 +2,7 @@ package e2e import ( "fmt" + "os" "strings" "testing" "time" @@ -182,7 +183,10 @@ func TestPublishServerError(t *testing.T) { ResponseStatusCode: 403, }) - pn := pubnub.NewPubNub(configCopy()) + config := pubnub.NewConfigWithUserId(pubnub.UserId(pubnub.GenerateUUID())) + config.PublishKey = os.Getenv("PUBLISH_KEY") + config.SubscribeKey = os.Getenv("SUBSCRIBE_KEY") + pn := pubnub.NewPubNub(config) pn.SetClient(interceptor.GetClient()) _, _, err := pn.Publish().Channel("ch").Message("hey").Execute() From 3a5f86c444f3a882552ea405873e3718ddd93d7d Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Thu, 28 Sep 2023 13:32:26 +0200 Subject: [PATCH 24/30] Remove deprecation --- utils/crypto.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/utils/crypto.go b/utils/crypto.go index 2441ef41..2ed5327a 100644 --- a/utils/crypto.go +++ b/utils/crypto.go @@ -13,7 +13,6 @@ import ( "strings" ) -// EncryptString DEPRECATED // EncryptString creates the base64 encoded encrypted string using the // cipherKey. // It accepts the following parameters: @@ -34,7 +33,6 @@ func EncryptString(cipherKey string, message string, useRandomInitializationVect return base64.StdEncoding.EncodeToString(encryptedData) } -// DecryptString DEPRECATED // DecryptString decodes encrypted string using the cipherKey // // It accepts the following parameters: @@ -99,7 +97,6 @@ func GetHmacSha256(secretKey string, input string) string { return signature } -// EncryptFile DEPRECATED func EncryptFile(cipherKey string, _ []byte, filePart io.Writer, file *os.File) { cryptor, e := crypto.NewLegacyCryptoModule(cipherKey, true) if e != nil { @@ -115,7 +112,6 @@ func EncryptFile(cipherKey string, _ []byte, filePart io.Writer, file *os.File) } } -// DecryptFile DEPRECATED func DecryptFile(cipherKey string, _ int64, reader io.Reader, w io.WriteCloser) { cryptoModule, e := crypto.NewLegacyCryptoModule(cipherKey, true) if e != nil { @@ -132,7 +128,6 @@ func DecryptFile(cipherKey string, _ int64, reader io.Reader, w io.WriteCloser) e = w.Close() } -// EncryptCipherKey DEPRECATED // EncryptCipherKey creates the 256 bit hex of the cipher key // // It accepts the following parameters: From 654155e6da5d90e64c00a21d88859eafcf3c3351 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Fri, 29 Sep 2023 11:36:51 +0200 Subject: [PATCH 25/30] Put correct cryptor data size if it's larger than 254 --- crypto/cryptor_header.go | 10 ++++++++-- crypto/cryptor_header_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 crypto/cryptor_header_test.go diff --git a/crypto/cryptor_header.go b/crypto/cryptor_header.go index 458342cf..2d47bdb0 100644 --- a/crypto/cryptor_header.go +++ b/crypto/cryptor_header.go @@ -3,8 +3,10 @@ package crypto import ( "bufio" "bytes" + "encoding/binary" "fmt" "io" + math "math" "strconv" ) @@ -27,8 +29,10 @@ func headerV1(cryptorId string, metadata []byte) ([]byte, error) { if cryptorDataSize < longSizeIndicator { cryptorDataBytesSize = shortSizeLength - } else { + } else if cryptorDataSize < math.MaxUint16 { cryptorDataBytesSize = longSizeLength + } else { + return nil, fmt.Errorf("size of cryptor metadata too large %d", cryptorDataSize) } r := make([]byte, 0, len(sentinel)+1+cryptorIdLength+cryptorDataBytesSize+cryptorDataSize) @@ -56,7 +60,9 @@ func headerV1(cryptorId string, metadata []byte) ([]byte, error) { if e != nil { return nil, e } - _, e = buffer.Write([]byte(strconv.FormatInt(int64(cryptorDataSize), 10))) //TODO + sizeBytes := make([]byte, 2) + binary.BigEndian.PutUint16(sizeBytes, uint16(cryptorDataSize)) + _, e = buffer.Write(sizeBytes) if e != nil { return nil, e } diff --git a/crypto/cryptor_header_test.go b/crypto/cryptor_header_test.go new file mode 100644 index 00000000..d2493ba4 --- /dev/null +++ b/crypto/cryptor_header_test.go @@ -0,0 +1,29 @@ +package crypto + +import ( + "bytes" + "github.com/stretchr/testify/assert" + "math" + "testing" +) + +func TestCryptorHeader_CreateHeaderWithLargeMetadata(t *testing.T) { + metadata := make([]byte, 512) + header, _ := headerV1("abcd", metadata) + cryptorDataSize := header[sentinelLength+1+cryptorIdLength+longSizeLength-2 : sentinelLength+1+cryptorIdLength+longSizeLength] + assert.True(t, bytes.Equal([]byte{0xff, 0x02, 0x00}, cryptorDataSize)) +} + +func TestCryptorHeader_CreateHeaderWithSmallMetadata(t *testing.T) { + metadata := make([]byte, 128) + header, _ := headerV1("abcd", metadata) + assert.Equal(t, byte(0x80), header[sizePosition]) +} + +func TestCryptorHeader_WithTooLargeMetadata(t *testing.T) { + metadata := make([]byte, math.MaxUint16+255) + _, e := headerV1("abcd", metadata) + if e == nil { + assert.Fail(t, "expected error") + } +} From 108c460d2dca6e6b0bb973a9e53ed81fe33afcd2 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Mon, 2 Oct 2023 10:15:31 +0200 Subject: [PATCH 26/30] Add proper info in Deprecation of UseRandomInitializationVector --- config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.go b/config.go index 42aaca95..03382cb6 100644 --- a/config.go +++ b/config.go @@ -51,7 +51,7 @@ type Config struct { UsePAMV3 bool // Use PAM version 2, Objects requets would still use PAM v3 StoreTokensOnGrant bool // Will store grant v3 tokens in token manager for further use. FileMessagePublishRetryLimit int // The number of tries made in case of Publish File Message failure. - //DEPRECATED: please use UseRandomInitializationVector + //DEPRECATED: please use CryptoModule UseRandomInitializationVector bool // When true the IV will be random for all requests and not just file upload. When false the IV will be hardcoded for all requests except File Upload CryptoModule crypto.CryptoModule // A cryptography module used for encryption and decryption } From 800988b4c56fafea6f70b30c041dcb52aeaea11b Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Mon, 2 Oct 2023 16:55:42 +0200 Subject: [PATCH 27/30] Include empty data checks --- crypto/aes_cbc_cryptor.go | 13 ++++--- crypto/cryptor_header.go | 5 +++ crypto/legacy_cryptor.go | 16 +++++++++ crypto/module.go | 53 +++++++++++++++++++++++++--- tests/contract/contract_test.go | 2 +- tests/contract/crypto_steps_test.go | 34 +++++++++--------- tests/contract/steps_mapping_test.go | 1 + tests/e2e/helper.go | 7 +++- tests/e2e/objectsV2_test.go | 42 ++++++++++++++++++++++ utils/crypto_test.go | 19 ++++++++++ 10 files changed, 161 insertions(+), 31 deletions(-) diff --git a/crypto/aes_cbc_cryptor.go b/crypto/aes_cbc_cryptor.go index e09712cd..98673aa4 100644 --- a/crypto/aes_cbc_cryptor.go +++ b/crypto/aes_cbc_cryptor.go @@ -49,18 +49,17 @@ func (c *aesCbcCryptor) Decrypt(encryptedData *EncryptedData) (r []byte, e error //to handle decryption errors defer func() { if rec := recover(); rec != nil { - r, e = nil, fmt.Errorf("decrypt error: %s", rec) + r, e = nil, fmt.Errorf("%s", rec) } }() - decrypted := make([]byte, len(encryptedData.Data)) - decrypter.CryptBlocks(decrypted, encryptedData.Data) - val, err := unpadPKCS7(decrypted) - if err != nil { - return nil, fmt.Errorf("decrypt error: %s", err) + if len(encryptedData.Data)%16 != 0 { + return nil, fmt.Errorf("encrypted data length should be divisible by block size") } - return val, nil + decrypted := make([]byte, len(encryptedData.Data)) + decrypter.CryptBlocks(decrypted, encryptedData.Data) + return unpadPKCS7(decrypted) } func (c *aesCbcCryptor) EncryptStream(reader io.Reader) (*EncryptedStreamData, error) { diff --git a/crypto/cryptor_header.go b/crypto/cryptor_header.go index 2d47bdb0..f391f50e 100644 --- a/crypto/cryptor_header.go +++ b/crypto/cryptor_header.go @@ -112,6 +112,10 @@ func parseHeader(data []byte) (cryptorId *string, encrData *EncryptedData, e err metadata := data[position : position+headerSize] position += int64(len(metadata)) + if int64(len(data)) < position { + return nil, nil, fmt.Errorf("decryption error: %w", e) + } + return id, &EncryptedData{Data: data[position:], Metadata: metadata}, nil } @@ -162,6 +166,7 @@ func parseHeaderStream(bufData *bufio.Reader) (cryptorId *string, encrypted *Enc if e != nil { return nil, nil, e } + return id, &EncryptedStreamData{ Reader: bufData, Metadata: m, diff --git a/crypto/legacy_cryptor.go b/crypto/legacy_cryptor.go index b76daeee..a7226fb9 100644 --- a/crypto/legacy_cryptor.go +++ b/crypto/legacy_cryptor.go @@ -1,11 +1,13 @@ package crypto import ( + "bufio" "bytes" "crypto/aes" "crypto/cipher" "crypto/sha256" "encoding/hex" + "errors" "fmt" "io" ) @@ -65,6 +67,10 @@ func (c *legacyCryptor) Decrypt(encryptedData *EncryptedData) (r []byte, e error iv = []byte(valIV) } + if len(data)%16 != 0 { + return nil, fmt.Errorf("length of data to decrypt should be divisible by block size") + } + decrypter := cipher.NewCBCDecrypter(c.block, iv) //to handle decryption errors defer func() { @@ -99,6 +105,16 @@ func (c *legacyCryptor) DecryptStream(encryptedData *EncryptedStreamData) (io.Re return nil, err } + bufReader := bufio.NewReader(encryptedData.Reader) + peeked, e := bufReader.Peek(1) + if len(peeked) == 0 { + return nil, errors.New("can't decrypt empty data") + } + + if e != nil { + return nil, e + } + return newBlockModeDecryptingReader(encryptedData.Reader, cipher.NewCBCDecrypter(c.block, iv)), nil } diff --git a/crypto/module.go b/crypto/module.go index d212a2cc..95229194 100644 --- a/crypto/module.go +++ b/crypto/module.go @@ -3,6 +3,7 @@ package crypto import ( "bufio" "bytes" + "errors" "fmt" "io" ) @@ -65,9 +66,12 @@ func NewCryptoModule(defaultCryptor Cryptor, decryptors []Cryptor) CryptoModule } func (m *module) Encrypt(message []byte) ([]byte, error) { + if len(message) == 0 { + return nil, errors.New("encryption error: can't encrypt empty data") + } encryptedData, e := m.encryptor.Encrypt(message) if e != nil { - return nil, e + return nil, fmt.Errorf("encryption error: %s", e.Error()) } if m.encryptor.Id() == legacyId { @@ -83,6 +87,10 @@ func (m *module) Encrypt(message []byte) ([]byte, error) { } func (m *module) Decrypt(data []byte) ([]byte, error) { + if len(data) == 0 { + return nil, errors.New("decryption error: can't decrypt empty data") + } + id, encryptedData, e := parseHeader(data) if e != nil { return nil, e @@ -94,11 +102,26 @@ func (m *module) Decrypt(data []byte) ([]byte, error) { return nil, fmt.Errorf("unknown crypto error: unknown cryptor id %s", *id) } - return decryptor.Decrypt(encryptedData) + if len(encryptedData.Data) == 0 { + return nil, errors.New("decryption error: can't decrypt empty data") + } + + var r []byte + if r, e = m.decryptors[*id].Decrypt(encryptedData); e != nil { + return nil, fmt.Errorf("decryption error: %s", e.Error()) + } + + return r, nil } func (m *module) EncryptStream(input io.Reader) (io.Reader, error) { - encryptedStreamData, e := m.encryptor.EncryptStream(input) + bufferedReader := bufio.NewReader(input) + peekedBytes, e := bufferedReader.Peek(1) + if len(peekedBytes) == 0 { + return nil, errors.New("encryption error: can't encrypt empty data") + } + + encryptedStreamData, e := m.encryptor.EncryptStream(bufferedReader) if e != nil { return nil, e } @@ -116,16 +139,36 @@ func (m *module) EncryptStream(input io.Reader) (io.Reader, error) { } func (m *module) DecryptStream(input io.Reader) (io.Reader, error) { + bufData := bufio.NewReader(input) - id, encryptedStreamData, e := parseHeaderStream(bufio.NewReader(input)) + id, encryptedStreamData, e := parseHeaderStream(bufData) if e != nil { return nil, e } + + peeked, e := bufData.Peek(1) + if e != nil { + return nil, fmt.Errorf("decryption error: %w", e) + } + + if len(peeked) == 0 { + return nil, errors.New("decryption error: can't decrypt empty data") + } + decryptor := m.decryptors[*id] if decryptor == nil { return nil, fmt.Errorf("unknown crypto error: unknown cryptor id %s", *id) } - return m.decryptors[*id].DecryptStream(encryptedStreamData) + if e != nil { + return nil, fmt.Errorf("decryption error: %s", e.Error()) + } + + var r io.Reader + if r, e = m.decryptors[*id].DecryptStream(encryptedStreamData); e != nil { + return nil, fmt.Errorf("decryption error: %s", e.Error()) + } + + return r, nil } diff --git a/tests/contract/contract_test.go b/tests/contract/contract_test.go index e02453c6..fd29f9aa 100644 --- a/tests/contract/contract_test.go +++ b/tests/contract/contract_test.go @@ -14,7 +14,7 @@ var format string func TestMain(m *testing.M) { flag.StringVar(&path, "path", "../../../sdk-specifications/features", "Path to feature files") - flag.StringVar(&tagsFilter, "tagsFilter", "~@skip && ~@na=go && ~@beta", "Tags filter") + flag.StringVar(&tagsFilter, "tagsFilter", "~@skip && ~@na=go && @beta && @featureSet=cryptoModule", "Tags filter") flag.StringVar(&format, "format", "pretty", "Output formatter") flag.Parse() if path == "" { diff --git a/tests/contract/crypto_steps_test.go b/tests/contract/crypto_steps_test.go index cbc7b634..cc46ef7a 100644 --- a/tests/contract/crypto_steps_test.go +++ b/tests/contract/crypto_steps_test.go @@ -85,20 +85,13 @@ func iDecryptFileAs(ctx context.Context, filename string, decryptionType string) } if decryptionType == "stream" { - cryptoState.resultReader, e = module.DecryptStream(bufio.NewReader(file)) - if e != nil { - return e - } - + cryptoState.resultReader, cryptoState.err = module.DecryptStream(bufio.NewReader(file)) } else { fileContent, e := io.ReadAll(file) if e != nil { return e } - cryptoState.result, e = module.Decrypt(fileContent) - if e != nil { - return e - } + cryptoState.result, cryptoState.err = module.Decrypt(fileContent) } return nil } @@ -144,19 +137,13 @@ func iEncryptFileAs(ctx context.Context, filename string, encryptionType string) return e } if encryptionType == "stream" { - cryptoState.resultReader, e = module.EncryptStream(bufio.NewReader(file)) - if e != nil { - return e - } + cryptoState.resultReader, cryptoState.err = module.EncryptStream(bufio.NewReader(file)) } else { content, e := io.ReadAll(file) if e != nil { return e } - cryptoState.result, e = module.Encrypt(content) - if e != nil { - return e - } + cryptoState.result, cryptoState.err = module.Encrypt(content) } return nil @@ -234,6 +221,19 @@ func successfullyDecryptAnEncryptedFileWithLegacyCode(ctx context.Context) error } } +func iReceiveEncryptionError(ctx context.Context) error { + cryptoState := getCryptoState(ctx) + if cryptoState.err != nil { + if strings.HasPrefix(cryptoState.err.Error(), "encryption error") { + return nil + } else { + return cryptoState.err + } + } else { + return errors.New("expected error") + } +} + type closerBuffer struct { *bytes.Buffer } diff --git a/tests/contract/steps_mapping_test.go b/tests/contract/steps_mapping_test.go index b5db113e..c69a3442 100644 --- a/tests/contract/steps_mapping_test.go +++ b/tests/contract/steps_mapping_test.go @@ -63,6 +63,7 @@ func MapSteps(ctx *godog.ScenarioContext) { ctx.Step(`^I receive \'decryption error\'$`, iReceiveDecryptionError) ctx.Step(`^I receive \'success\'$`, iReceiveSuccess) ctx.Step(`^I receive \'unknown cryptor error\'$`, iReceiveUnknownCryptoError) + ctx.Step(`^I receive \'encryption error\'$`, iReceiveEncryptionError) ctx.Step(`^with \'(.*)\' cipher key$`, withCipherKey) ctx.Step(`^with \'(.*)\' vector$`, withVector) diff --git a/tests/e2e/helper.go b/tests/e2e/helper.go index fe0309d0..73685a26 100644 --- a/tests/e2e/helper.go +++ b/tests/e2e/helper.go @@ -11,13 +11,14 @@ import ( "net" "net/http" "os" + "testing" "time" pubnub "github.com/pubnub/go/v7" "github.com/stretchr/testify/assert" ) -var enableDebuggingInTests = false +var enableDebuggingInTests = true const ( SPECIAL_CHARACTERS = "-.,_~:/?#[]@!$&'()*+;=`|" @@ -99,6 +100,10 @@ func logInTest(format string, a ...interface{}) (n int, err error) { return 0, nil } +func checkForAsserted(t *testing.T, maxTime, intervalTime time.Duration, fun func() error) { + +} + func checkFor(assert *assert.Assertions, maxTime, intervalTime time.Duration, fun func() error) { maxTimeout := time.NewTimer(maxTime) interval := time.NewTicker(intervalTime) diff --git a/tests/e2e/objectsV2_test.go b/tests/e2e/objectsV2_test.go index d29d7468..a2d36069 100644 --- a/tests/e2e/objectsV2_test.go +++ b/tests/e2e/objectsV2_test.go @@ -1,6 +1,7 @@ package e2e import ( + "errors" "fmt" "log" "os" @@ -1014,6 +1015,36 @@ func ObjectsMembershipsCommonV2(t *testing.T, withPAM, runWithoutSecretKey bool) pubnub.PNChannelMetadataIncludeCustom, } + checkFor(assert, time.Second, time.Millisecond*200, func() error { + res2, st2, err2 := pn.SetChannelMetadata().Include(inclChannel).Channel(channelid).Name(name).Description(desc).Custom(custom2).Execute() + if err2 != nil { + return err2 + } + if 200 != st2.StatusCode { + return fmt.Errorf("expected status code 200 but got %d", st2.StatusCode) + } + if res2 != nil { + if channelid != res2.Data.ID { + return fmt.Errorf("expected channelId %s but got %s", channelid, res2.Data.ID) + } + if name != res2.Data.Name { + return fmt.Errorf("expected name %s but got %s", name, res2.Data.Name) + } + if desc != res2.Data.Description { + return fmt.Errorf("expected desc %s but got %s", desc, res2.Data.Description) + } + //assert.NotNil(res2.Data.Updated) + //assert.NotNil(res2.Data.ETag) + if "b1" != res2.Data.Custom["a1"] { + return fmt.Errorf("expected custom a1 -> b1 but got a1 -> %v", res2.Data.Custom["a1"]) + } + if "d1" != res2.Data.Custom["c1"] { + return fmt.Errorf("expected custom a1 -> b1 but got a1 -> %v", res2.Data.Custom["a1"]) + } + return nil + } + return errors.New("expected res to be not nil") + }) res2, st2, err2 := pn.SetChannelMetadata().Include(inclChannel).Channel(channelid).Name(name).Description(desc).Custom(custom2).Execute() assert.Nil(err2) assert.Equal(200, st2.StatusCode) @@ -1918,3 +1949,14 @@ func ObjectsListenersCommonV2(t *testing.T, withPAM, runWithoutSecretKey bool) { exitListener <- true } + +func Test_CleanUpTestEnv(t *testing.T) { + pn := pubnub.NewPubNub(configCopy()) + execute, _, err := pn.GetAllChannelMetadata().Execute() + if err != nil { + t.Error(err.Error()) + return + } + + fmt.Printf("%v", execute) +} diff --git a/utils/crypto_test.go b/utils/crypto_test.go index 885ca613..b7b6236e 100644 --- a/utils/crypto_test.go +++ b/utils/crypto_test.go @@ -489,3 +489,22 @@ func CreateLoggerForTests() *log.Logger { } return infoLogger } + +func TestDecryptStuffFromPython(t *testing.T) { + cipherKey := "myCipherKey" + s1 := "KGc+SNJD7mIveY+KNIL/L9ZzAjC0dCJCju+HXRwSW2k=" + s2 := "PXjHv0L05kgj0mqIE9s7n4LDPrLtjnfamMoHyiMoL0R1uzSMsYp7dDfqEWrnoaqS" + s3 := "UE5FRAFBQ1JIEHvl3cY3RYsHnbKm6VR51XG/Y7HodnkumKHxo+mrsxbIjZvFpVuILQ0oZysVwjNsDNMKiMfZteoJ8P1/mvPmbuQKLErBzS2l7vEohCwbmAJODPR2yNhJGB8989reTZ7Y7Q==" + + d1, _ := DecryptString(cipherKey, s1, false) + d2, _ := DecryptString(cipherKey, s2, true) + d3, _ := DecryptString(cipherKey, s3, true) + + r1 := d1.(string) + r2 := d2.(string) + r3 := d3.(string) + + fmt.Printf("%s: => %d\n", r1, len(r1)) + fmt.Printf("%s: => %d\n", r2, len(r2)) + fmt.Printf("%s: => %d\n", r3, len(r3)) +} From 91718dece5a7bb65a06e839476201b931bea8bab Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Mon, 2 Oct 2023 17:10:02 +0200 Subject: [PATCH 28/30] Fix fire request In case of cipher key being present we would drop users message and encrypt only padding --- fire_request.go | 20 ++++++++++---------- fire_request_test.go | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/fire_request.go b/fire_request.go index e5a631e1..5481c7b6 100644 --- a/fire_request.go +++ b/fire_request.go @@ -158,17 +158,17 @@ func (o *fireOpts) buildPath() (string, error) { var err error if o.pubnub.getCryptoModule() != nil { - msg, e := encryptString(o.pubnub.getCryptoModule(), string(message)) - if e != nil { - return "", e + var msg string + if msg, err = serializeEncryptAndSerialize(o.pubnub.getCryptoModule(), o.Message, o.Serialize); err != nil { + o.pubnub.Config.Log.Printf("error in serializing: %v\n", err) + return "", err + } + message = []byte(msg) + } else { + message, err = utils.ValueAsString(o.Message) + if err != nil { + return "", err } - - o.Message = []byte(msg) - } - - message, err = utils.ValueAsString(o.Message) - if err != nil { - return "", err } return fmt.Sprintf(publishGetPath, diff --git a/fire_request_test.go b/fire_request_test.go index 5f3d5e2a..5ad48b7b 100644 --- a/fire_request_test.go +++ b/fire_request_test.go @@ -308,7 +308,7 @@ func TestFireGetAllParameters(t *testing.T) { } func TestFireGetAllParametersCipher(t *testing.T) { message := "test" - AssertSuccessFireGetAllParameters(t, "%22c3dSanMrRnc4ZnNNT1BEaGFnZmd1QT09%22", message, "enigma") + AssertSuccessFireGetAllParameters(t, "%22%2B3AfkVAl8saHsXJdtOhRVQ%3D%3D%22", message, "enigma") } func TestFirePostAllParameters(t *testing.T) { From a327a2627c68f0bf1a23e00e308175243f4b3f39 Mon Sep 17 00:00:00 2001 From: Lukasz Klich Date: Tue, 3 Oct 2023 13:10:18 +0200 Subject: [PATCH 29/30] Simplify the objectsV2 tests significantly --- tests/e2e/helper.go | 4 +- tests/e2e/objectsV2_test.go | 1998 ++--------------------------------- 2 files changed, 116 insertions(+), 1886 deletions(-) diff --git a/tests/e2e/helper.go b/tests/e2e/helper.go index 73685a26..a0c2c036 100644 --- a/tests/e2e/helper.go +++ b/tests/e2e/helper.go @@ -18,7 +18,7 @@ import ( "github.com/stretchr/testify/assert" ) -var enableDebuggingInTests = true +var enableDebuggingInTests = false const ( SPECIAL_CHARACTERS = "-.,_~:/?#[]@!$&'()*+;=`|" @@ -101,7 +101,7 @@ func logInTest(format string, a ...interface{}) (n int, err error) { } func checkForAsserted(t *testing.T, maxTime, intervalTime time.Duration, fun func() error) { - + } func checkFor(assert *assert.Assertions, maxTime, intervalTime time.Duration, fun func() error) { diff --git a/tests/e2e/objectsV2_test.go b/tests/e2e/objectsV2_test.go index a2d36069..13c1c5b9 100644 --- a/tests/e2e/objectsV2_test.go +++ b/tests/e2e/objectsV2_test.go @@ -1,1962 +1,192 @@ package e2e import ( - "errors" - "fmt" + pubnub "github.com/pubnub/go/v7" + "github.com/stretchr/testify/assert" "log" "os" - "sync" + "reflect" "testing" - "time" - - pubnub "github.com/pubnub/go/v7" - "github.com/stretchr/testify/assert" ) -func ActivateWithPAMV2() *pubnub.PubNub { - pn := pubnub.NewPubNub(pamConfigCopy()) - return pn -} - -func RunGrantV2(pn *pubnub.PubNub, users, channels []string, read, write, manage, del, create, createPattern bool) []string { - authkey := randomized("authkey") - - res, _, _ := pn.Grant(). - Read(true).Write(true).Manage(true). - Get(true).Update(true).Join(true). - UUIDs(append(users, channels...)).AuthKeys([]string{authkey}). - Execute() - - if res != nil { - return []string{authkey} - } - return []string{} -} - -func SetPNV2(pn, pn2 *pubnub.PubNub, tokens []string) { - pn.Config.SubscribeKey = pn2.Config.SubscribeKey - pn.Config.PublishKey = pn2.Config.PublishKey - pn.Config.SecretKey = "" - pn.Config.Origin = pn2.Config.Origin - pn.Config.Secure = pn2.Config.Secure - - pn.Config.AuthKey = tokens[0] -} - -func TestObjectsV2CreateUpdateGetDeleteUUID(t *testing.T) { - ObjectsCreateUpdateGetDeleteUUIDCommon(t, false, false) -} - -func TestObjectsV2CreateUpdateGetDeleteUUIDWithPAM(t *testing.T) { - ObjectsCreateUpdateGetDeleteUUIDCommon(t, true, false) -} - -// func TestObjectsV2CreateUpdateGetDeleteUUIDWithPAMWithoutSecKey(t *testing.T) { -// ObjectsCreateUpdateGetDeleteUUIDCommon(t, true, true) -// } - -func ObjectsCreateUpdateGetDeleteUUIDCommon(t *testing.T, withPAM, runWithoutSecretKey bool) { - - assert := assert.New(t) +func TestObjectsV2ChannelMetadataSetUpdateGetRemove(t *testing.T) { + a := assert.New(t) pn := pubnub.NewPubNub(configCopy()) - id := randomized("testuser") - if withPAM { - pn2 := ActivateWithPAMV2() - if runWithoutSecretKey { - tokens := RunGrantV2(pn2, []string{id}, []string{}, true, true, true, true, true, true) - SetPNV2(pn, pn2, tokens) - } else { - pn = pn2 - RunGrantV2(pn, []string{id}, []string{}, true, true, false, true, true, false) - } - - } - - if enableDebuggingInTests { - pn.Config.Log = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) - } - - name := randomized("name") - extid := "extid" - purl := "profileurl" - email := "email" - - custom := make(map[string]interface{}) - custom["a"] = "b" - custom["c"] = "d" - - incl := []pubnub.PNUUIDMetadataInclude{ - pubnub.PNUUIDMetadataIncludeCustom, - } - - res, st, err := pn.SetUUIDMetadata().Include(incl).UUID(id).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(custom).Execute() - assert.Nil(err) - assert.Equal(200, st.StatusCode) - if res != nil { - assert.Equal(id, res.Data.ID) - assert.Equal(name, res.Data.Name) - assert.Equal(extid, res.Data.ExternalID) - assert.Equal(purl, res.Data.ProfileURL) - assert.Equal(email, res.Data.Email) - // assert.NotNil(res.Data.Created) - assert.NotNil(res.Data.Updated) - assert.NotNil(res.Data.ETag) - assert.Equal("b", res.Data.Custom["a"]) - assert.Equal("d", res.Data.Custom["c"]) - } - - email = "email2" - - res2, st2, err2 := pn.SetUUIDMetadata().Include(incl).UUID(id).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(custom).Execute() - assert.Nil(err2) - assert.Equal(200, st2.StatusCode) - if res2 != nil { - assert.Equal(id, res2.Data.ID) - assert.Equal(name, res2.Data.Name) - assert.Equal(extid, res2.Data.ExternalID) - assert.Equal(purl, res2.Data.ProfileURL) - assert.Equal(email, res2.Data.Email) - // assert.Equal(res.Data.Created, res2.Data.Created) - assert.NotNil(res2.Data.Updated) - assert.NotNil(res2.Data.ETag) - assert.Equal("b", res2.Data.Custom["a"]) - assert.Equal("d", res2.Data.Custom["c"]) - } - - res3, st3, err3 := pn.GetUUIDMetadata().UUID(id).Include(incl).Execute() - assert.Nil(err3) - assert.Equal(200, st3.StatusCode) - if res3 != nil { - assert.Equal(id, res3.Data.ID) - assert.Equal(name, res3.Data.Name) - assert.Equal(extid, res3.Data.ExternalID) - assert.Equal(purl, res3.Data.ProfileURL) - assert.Equal(email, res3.Data.Email) - // assert.Equal(res.Data.Created, res3.Data.Created) - if res2 != nil { - assert.Equal(res2.Data.Updated, res3.Data.Updated) - assert.Equal(res2.Data.ETag, res3.Data.ETag) - } - assert.Equal("b", res3.Data.Custom["a"]) - assert.Equal("d", res3.Data.Custom["c"]) - } - - //getusers - sort := []string{"updated:desc"} - if withPAM { - res6, st6, err6 := pn.GetAllUUIDMetadata().Include(incl).Sort(sort).Limit(100).Count(true).Execute() - assert.Nil(err6) - assert.Equal(200, st6.StatusCode) - assert.True(res6.TotalCount > 0) - found := false - for i := range res6.Data { - if res6.Data[i].ID == id { - assert.Equal(name, res6.Data[i].Name) - assert.Equal(extid, res6.Data[i].ExternalID) - assert.Equal(purl, res6.Data[i].ProfileURL) - assert.Equal(email, res6.Data[i].Email) - // assert.Equal(res.Data.Created, res6.Data[i].Created) - if res2 != nil { - assert.Equal(res2.Data.Updated, res6.Data[i].Updated) - assert.Equal(res2.Data.ETag, res6.Data[i].ETag) - } - assert.Equal("b", res6.Data[i].Custom["a"]) - assert.Equal("d", res6.Data[i].Custom["c"]) - found = true - } - } - assert.True(found) - - res6F, st6F, err6F := pn.GetAllUUIDMetadata().Include(incl).Limit(100).Filter("name == '" + name + "'").Count(true).Execute() - assert.Nil(err6F) - assert.Equal(200, st6F.StatusCode) - assert.True(res6F.TotalCount > 0) - foundF := false - for i := range res6F.Data { - //fmt.Println(res6F.Data[i], id) - if res6F.Data[i].ID == id { - assert.Equal(name, res6F.Data[i].Name) - assert.Equal(extid, res6F.Data[i].ExternalID) - assert.Equal(purl, res6F.Data[i].ProfileURL) - assert.Equal(email, res6F.Data[i].Email) - // assert.Equal(res.Data.Created, res6F.Data[i].Created) - assert.Equal(res2.Data.Updated, res6F.Data[i].Updated) - assert.Equal(res2.Data.ETag, res6F.Data[i].ETag) - assert.Equal("b", res6F.Data[i].Custom["a"]) - assert.Equal("d", res6F.Data[i].Custom["c"]) - foundF = true - } - } - assert.True(foundF) - } - - //delete - res5, st5, err5 := pn.RemoveUUIDMetadata().UUID(id).Execute() - assert.Nil(err5) - assert.Equal(200, st5.StatusCode) - if res5 != nil { - assert.Nil(res5.Data) - } - - //getuser - res4, st4, err4 := pn.GetUUIDMetadata().Include(incl).UUID(id).Execute() - assert.NotNil(err4) - if res5 != nil { - assert.Nil(res4) - } - assert.Equal(404, st4.StatusCode) - -} - -func TestObjectsV2CreateUpdateGetDeleteChannel(t *testing.T) { - ObjectsCreateUpdateGetDeleteChannelCommon(t, false, false) -} - -func TestObjectsV2CreateUpdateGetDeleteChannelWithPAM(t *testing.T) { - ObjectsCreateUpdateGetDeleteChannelCommon(t, true, false) -} - -// func TestObjectsV2CreateUpdateGetDeleteChannelWithPAMWithoutSecKey(t *testing.T) { -// ObjectsCreateUpdateGetDeleteChannelCommon(t, true, true) -// } - -func ObjectsCreateUpdateGetDeleteChannelCommon(t *testing.T, withPAM, runWithoutSecretKey bool) { - assert := assert.New(t) - - pn := pubnub.NewPubNub(configCopy()) id := randomized("testchannel") - - if withPAM { - pn2 := ActivateWithPAMV2() - if runWithoutSecretKey { - tokens := RunGrantV2(pn2, []string{}, []string{id}, true, true, false, true, true, true) - SetPNV2(pn, pn2, tokens) - } else { - pn = pn2 - RunGrantV2(pn, []string{}, []string{id}, true, true, false, true, true, false) - } - - } if enableDebuggingInTests { pn.Config.Log = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) } name := randomized("name") desc := "desc" - - custom := make(map[string]interface{}) - custom["a"] = "b" - custom["c"] = "d" + custom := map[string]interface{}{"a": "b", "c": "d"} incl := []pubnub.PNChannelMetadataInclude{ pubnub.PNChannelMetadataIncludeCustom, } + defer removeChannelMetadata(a, pn, id) res, st, err := pn.SetChannelMetadata().Include(incl).Channel(id).Name(name).Description(desc).Custom(custom).Execute() - assert.Nil(err) - assert.Equal(200, st.StatusCode) + + a.Nil(err) + a.Equal(200, st.StatusCode) if res != nil { - assert.Equal(id, res.Data.ID) - assert.Equal(name, res.Data.Name) - assert.Equal(desc, res.Data.Description) - // assert.NotNil(res.Data.Created) - assert.NotNil(res.Data.Updated) - assert.NotNil(res.Data.ETag) - assert.Equal("b", res.Data.Custom["a"]) - assert.Equal("d", res.Data.Custom["c"]) + a.Equal(id, res.Data.ID) + a.Equal(name, res.Data.Name) + a.Equal(desc, res.Data.Description) + a.NotNil(res.Data.Updated) + a.NotNil(res.Data.ETag) + a.True(reflect.DeepEqual(custom, res.Data.Custom)) } desc = "desc2" - res2, st2, err2 := pn.SetChannelMetadata().Include(incl).Channel(id).Name(name).Description(desc).Custom(custom).Execute() - assert.Nil(err2) - assert.Equal(200, st2.StatusCode) - if res2 != nil { - assert.Equal(id, res2.Data.ID) - assert.Equal(name, res2.Data.Name) - assert.Equal(desc, res2.Data.Description) - // assert.Equal(res.Data.Created, res2.Data.Created) - assert.NotNil(res2.Data.Updated) - assert.NotNil(res2.Data.ETag) - assert.Equal("b", res2.Data.Custom["a"]) - assert.Equal("d", res2.Data.Custom["c"]) - } - - res3, st3, err3 := pn.GetChannelMetadata().Include(incl).Channel(id).Execute() - assert.Nil(err3) - assert.Equal(200, st3.StatusCode) - if res3 != nil { - assert.Equal(id, res3.Data.ID) - assert.Equal(name, res3.Data.Name) - assert.Equal(desc, res3.Data.Description) - // assert.Equal(res.Data.Created, res3.Data.Created) - assert.Equal(res2.Data.Updated, res3.Data.Updated) - assert.Equal(res2.Data.ETag, res3.Data.ETag) - assert.Equal("b", res3.Data.Custom["a"]) - assert.Equal("d", res3.Data.Custom["c"]) - } - - sort := []string{"updated:desc"} - //getusers - if withPAM { - res6, st6, err6 := pn.GetAllChannelMetadata().Include(incl).Sort(sort).Limit(100).Count(true).Execute() - assert.Nil(err6) - assert.Equal(200, st6.StatusCode) - found := false - if res6 != nil { - assert.True(res6.TotalCount > 0) - - for i := range res6.Data { - if res6.Data[i].ID == id { - assert.Equal(name, res6.Data[i].Name) - assert.Equal(desc, res6.Data[i].Description) - // assert.Equal(res.Data.Created, res6.Data[i].Created) - assert.Equal(res2.Data.Updated, res6.Data[i].Updated) - assert.Equal(res2.Data.ETag, res6.Data[i].ETag) - assert.Equal("b", res6.Data[i].Custom["a"]) - assert.Equal("d", res6.Data[i].Custom["c"]) - found = true - } - } - } - assert.True(found) - - res6F, st6F, err6F := pn.GetAllChannelMetadata().Include(incl).Limit(100).Filter("name like '" + name + "*'").Count(true).Execute() - assert.Nil(err6F) - assert.Equal(200, st6F.StatusCode) - foundF := false - if res6F != nil { - assert.True(res6F.TotalCount > 0) - - for i := range res6F.Data { - if res6F.Data[i].ID == id { - assert.Equal(name, res6F.Data[i].Name) - assert.Equal(desc, res6F.Data[i].Description) - // assert.Equal(res.Data.Created, res6F.Data[i].Created) - assert.Equal(res2.Data.Updated, res6F.Data[i].Updated) - assert.Equal(res2.Data.ETag, res6F.Data[i].ETag) - assert.Equal("b", res6F.Data[i].Custom["a"]) - assert.Equal("d", res6F.Data[i].Custom["c"]) - foundF = true - } - } - } - assert.True(foundF) - - } - - //delete - res5, st5, err5 := pn.RemoveChannelMetadata().Channel(id).Execute() - assert.Nil(err5) - assert.Equal(200, st5.StatusCode) - if res5 != nil { - assert.Nil(res5.Data) - } - - //getuser - res4, st4, err4 := pn.GetChannelMetadata().Include(incl).Channel(id).Execute() - assert.NotNil(err4) - if res4 != nil { - assert.Nil(res4) + res, st, err = pn.SetChannelMetadata().Include(incl).Channel(id).Name(name).Description(desc).Custom(custom).Execute() + a.Nil(err) + a.Equal(200, st.StatusCode) + if res != nil { + a.Equal(id, res.Data.ID) + a.Equal(desc, res.Data.Description) } - assert.Equal(404, st4.StatusCode) -} + _, st, err = pn.GetChannelMetadata().Include(incl).Channel(id).Execute() + a.Nil(err) + a.Equal(200, st.StatusCode) -func TestObjectsV2SetRemoveMembershipsV2(t *testing.T) { - ObjectsSetRemoveMembershipsCommonV2(t, false, false) } -func TestObjectsV2SetRemoveMembershipsV2WithPAM(t *testing.T) { - ObjectsSetRemoveMembershipsCommonV2(t, true, false) -} - -// PASSES after adding PAM checks for Update Members -// func TestObjectsV2SetRemoveMembershipsV2WithPAMWithoutSecKey(t *testing.T) { -// ObjectsSetRemoveMembershipsCommonV2(t, true, true) -// } - -func ObjectsSetRemoveMembershipsCommonV2(t *testing.T, withPAM, runWithoutSecretKey bool) { - assert := assert.New(t) - - limit := 100 - count := true - - pn := pubnub.NewPubNub(configCopy()) - - userid := randomized("testuser1") - channelid := randomized("testchannel") - - if withPAM { - pn2 := ActivateWithPAMV2() - if runWithoutSecretKey { - tokens := RunGrantV2(pn2, []string{userid}, []string{channelid}, true, true, true, true, true, true) - SetPNV2(pn, pn2, tokens) - } else { - pn = pn2 - RunGrantV2(pn, []string{userid}, []string{channelid}, true, true, true, true, true, false) - } - - } - if enableDebuggingInTests { - pn.Config.Log = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) - } - - name := randomized("name") - extid := "extid" - purl := "profileurl" - email := "email" - - custom := make(map[string]interface{}) - custom["a"] = "b" - custom["c"] = "d" - - inclUUID := []pubnub.PNUUIDMetadataInclude{ - pubnub.PNUUIDMetadataIncludeCustom, - } - - res, st, err := pn.SetUUIDMetadata().Include(inclUUID).UUID(userid).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(custom).Execute() - assert.Nil(err) - assert.Equal(200, st.StatusCode) +func removeChannelMetadata(a *assert.Assertions, pn *pubnub.PubNub, id string) { + res, st, err := pn.RemoveChannelMetadata().Channel(id).Execute() + a.Nil(err) + a.Equal(200, st.StatusCode) if res != nil { - assert.Equal(userid, res.Data.ID) - assert.Equal(name, res.Data.Name) - assert.Equal(extid, res.Data.ExternalID) - assert.Equal(purl, res.Data.ProfileURL) - assert.Equal(email, res.Data.Email) - // assert.NotNil(res.Data.Created) - assert.NotNil(res.Data.Updated) - assert.NotNil(res.Data.ETag) - assert.Equal("b", res.Data.Custom["a"]) - assert.Equal("d", res.Data.Custom["c"]) - } - - desc := "desc" - custom2 := make(map[string]interface{}) - custom2["a1"] = "b1" - custom2["c1"] = "d1" - - inclChannel := []pubnub.PNChannelMetadataInclude{ - pubnub.PNChannelMetadataIncludeCustom, - } - - res2, st2, err2 := pn.SetChannelMetadata().Include(inclChannel).Channel(channelid).Name(name).Description(desc).Custom(custom2).Execute() - assert.Nil(err2) - assert.Equal(200, st2.StatusCode) - //fmt.Println("res2-->", res2) - if res2 != nil { - assert.Equal(channelid, res2.Data.ID) - assert.Equal(name, res2.Data.Name) - assert.Equal(desc, res2.Data.Description) - // assert.NotNil(res2.Data.Created) - assert.NotNil(res2.Data.Updated) - assert.NotNil(res2.Data.ETag) - assert.Equal("b1", res2.Data.Custom["a1"]) - assert.Equal("d1", res2.Data.Custom["c1"]) + a.Nil(res.Data) } - - userid2 := randomized("testuser2") - - _, st3, err3 := pn.SetUUIDMetadata().Include(inclUUID).UUID(userid2).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(custom).Execute() - assert.Nil(err3) - assert.Equal(200, st3.StatusCode) - - channelid2 := randomized("testchannel") - - _, st4, err4 := pn.SetChannelMetadata().Include(inclChannel).Channel(channelid2).Name(name).Description(desc).Custom(custom2).Execute() - assert.Nil(err4) - assert.Equal(200, st4.StatusCode) - - userid3 := randomized("testuser3") - - _, stuser3, erruser3 := pn.SetUUIDMetadata().Include(inclUUID).UUID(userid3).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(custom).Execute() - assert.Nil(erruser3) - assert.Equal(200, stuser3.StatusCode) - - channelid3 := randomized("testchannel") - - _, stchannel3, errchannel33 := pn.SetChannelMetadata().Include(inclChannel).Channel(channelid3).Name(name).Description(desc).Custom(custom2).Execute() - assert.Nil(errchannel33) - assert.Equal(200, stchannel3.StatusCode) - - inclSm := []pubnub.PNChannelMembersInclude{ - pubnub.PNChannelMembersIncludeUUIDCustom, - pubnub.PNChannelMembersIncludeUUID, - pubnub.PNChannelMembersIncludeCustom, - } - - custom3 := make(map[string]interface{}) - custom3["a3"] = "b3" - custom3["c3"] = "d3" - - uuid := pubnub.PNChannelMembersUUID{ - ID: userid, - } - - in := pubnub.PNChannelMembersSet{ - UUID: uuid, - Custom: custom3, - } - uuid3 := pubnub.PNChannelMembersUUID{ - ID: userid3, - } - - inUser3 := pubnub.PNChannelMembersSet{ - UUID: uuid3, - Custom: custom3, - } - - inArr := []pubnub.PNChannelMembersSet{ - in, - inUser3, - } - - //Add Channel Memberships - sortSetChannelMembers := []string{"uuid.id:desc"} - sort := []string{"updated:desc"} - - resAdd, stAdd, errAdd := pn.SetChannelMembers().Channel(channelid).Sort(sortSetChannelMembers).Set(inArr).Include(inclSm).Limit(limit).Count(count).Execute() - assert.Nil(errAdd) - assert.Equal(200, stAdd.StatusCode) - if errAdd == nil { - sortMembers1 := false - sortMembers2 := false - - found := false - assert.True(resAdd.TotalCount > 0) - //fmt.Println("resAdd-->", resAdd) - - for i := range resAdd.Data { - if resAdd.Data[i].UUID.ID == userid { - found = true - assert.Equal(custom3["a3"], resAdd.Data[i].Custom["a3"]) - assert.Equal(custom3["c3"], resAdd.Data[i].Custom["c3"]) - assert.Equal(userid, resAdd.Data[i].UUID.ID) - assert.Equal(name, resAdd.Data[i].UUID.Name) - assert.Equal(extid, resAdd.Data[i].UUID.ExternalID) - assert.Equal(purl, resAdd.Data[i].UUID.ProfileURL) - assert.Equal(email, resAdd.Data[i].UUID.Email) - assert.Equal(custom["a"], resAdd.Data[i].UUID.Custom["a"]) - assert.Equal(custom["c"], resAdd.Data[i].UUID.Custom["c"]) - } - } - if (resAdd.Data != nil) && (len(resAdd.Data) > 1) { - sortMembers1 = (resAdd.Data[1].UUID.ID == userid) - sortMembers2 = (resAdd.Data[0].UUID.ID == userid3) - assert.True(sortMembers1) - assert.True(sortMembers2) - } else { - assert.Fail("Sort ", "resAdd.Data null or ", len(resAdd.Data)) - } - - assert.True(found) - } else { - if enableDebuggingInTests { - - fmt.Println("ManageMembers->", errAdd.Error()) - } - } - - //Update Channel Memberships - if !withPAM { - - custom4 := make(map[string]interface{}) - custom4["a2"] = "b2" - custom4["c2"] = "d2" - - up := pubnub.PNChannelMembersSet{ - UUID: uuid, - Custom: custom4, - } - - upArr := []pubnub.PNChannelMembersSet{ - up, - } - - resUp, stUp, errUp := pn.SetChannelMembers().Channel(channelid).Sort(sort).Set(upArr).Include(inclSm).Limit(limit).Count(count).Execute() - assert.Nil(errUp) - assert.Equal(200, stUp.StatusCode) - if errUp == nil { - assert.True(resUp.TotalCount > 0) - foundUp := false - for i := range resUp.Data { - if resUp.Data[i].UUID.ID == userid { - foundUp = true - assert.Equal("b2", resUp.Data[i].Custom["a2"]) - assert.Equal("d2", resUp.Data[i].Custom["c2"]) - //assert.Equal(userid, resAdd.Data[i].UUID.ID) - assert.Equal(name, resAdd.Data[i].UUID.Name) - assert.Equal(extid, resAdd.Data[i].UUID.ExternalID) - assert.Equal(purl, resAdd.Data[i].UUID.ProfileURL) - assert.Equal(email, resAdd.Data[i].UUID.Email) - assert.Equal(custom["a"], resAdd.Data[i].UUID.Custom["a"]) - assert.Equal(custom["c"], resAdd.Data[i].UUID.Custom["c"]) - - } - } - assert.True(foundUp) - } else { - if enableDebuggingInTests { - - fmt.Println("ManageMembers->", errUp.Error()) - } - } - } - //Get Channel Memberships - - inclMemberships := []pubnub.PNMembershipsInclude{ - pubnub.PNMembershipsIncludeCustom, - pubnub.PNMembershipsIncludeChannel, - pubnub.PNMembershipsIncludeChannelCustom, - } - - //fmt.Println("GetMemberships ====>") - - resGetMem, stGetMem, errGetMem := pn.GetMemberships().UUID(userid).Include(inclMemberships).Sort(sort).Limit(limit).Count(count).Execute() - foundGetMem := false - assert.Nil(errGetMem) - if errGetMem == nil { - for i := range resGetMem.Data { - if resGetMem.Data[i].Channel.ID == channelid { - foundGetMem = true - assert.Equal(name, resGetMem.Data[i].Channel.Name) - assert.Equal(desc, resGetMem.Data[i].Channel.Description) - assert.Equal("b1", resGetMem.Data[i].Channel.Custom["a1"]) - assert.Equal("d1", resGetMem.Data[i].Channel.Custom["c1"]) - if withPAM { - assert.Equal("b3", resGetMem.Data[i].Custom["a3"]) - assert.Equal("d3", resGetMem.Data[i].Custom["c3"]) - } else { - assert.Equal("b2", resGetMem.Data[i].Custom["a2"]) - assert.Equal("d2", resGetMem.Data[i].Custom["c2"]) - } - } - } - assert.Equal(200, stGetMem.StatusCode) - assert.True(foundGetMem) - } else { - if enableDebuggingInTests { - fmt.Println("GetMemberships->", errGetMem.Error()) - } - } - - //filterExp := fmt.Sprintf("custom.c3 == '%s' || custom.c2 == '%s'", "d3", "d2") - filterExp := fmt.Sprintf("channel.name == '%s'", name) - - //fmt.Println("GetMemberships ====>", filterExp) - - resGetMemF, stGetMemF, errGetMemF := pn.GetMemberships().UUID(userid).Include(inclMemberships).Filter(filterExp).Limit(limit).Count(count).Execute() - foundGetMemF := false - assert.Nil(errGetMemF) - if errGetMemF == nil { - for i := range resGetMemF.Data { - if resGetMemF.Data[i].Channel.ID == channelid { - foundGetMemF = true - assert.Equal(name, resGetMemF.Data[i].Channel.Name) - assert.Equal(desc, resGetMemF.Data[i].Channel.Description) - assert.Equal("b1", resGetMemF.Data[i].Channel.Custom["a1"]) - assert.Equal("d1", resGetMemF.Data[i].Channel.Custom["c1"]) - if withPAM { - assert.Equal("b3", resGetMemF.Data[i].Custom["a3"]) - assert.Equal("d3", resGetMemF.Data[i].Custom["c3"]) - } else { - assert.Equal("b2", resGetMemF.Data[i].Custom["a2"]) - assert.Equal("d2", resGetMemF.Data[i].Custom["c2"]) - } - } - } - assert.Equal(200, stGetMemF.StatusCode) - assert.True(foundGetMemF) - } else { - if enableDebuggingInTests { - - fmt.Println("GetMemberships->", errGetMemF.Error()) - } - } - - //Remove Channel Memberships - re := pubnub.PNChannelMembersRemove{ - UUID: uuid, - } - - reArr := []pubnub.PNChannelMembersRemove{ - re, - } - resRem, stRem, errRem := pn.RemoveChannelMembers().Channel(channelid).Remove(reArr).Include(inclSm).Limit(limit).Count(count).Execute() - assert.Nil(errRem) - assert.Equal(200, stRem.StatusCode) - //fmt.Println("====>stRem.StatusCode", stRem.StatusCode) - if errRem == nil { - - foundRem := false - for i := range resRem.Data { - if resRem.Data[i].UUID.ID == userid { - foundRem = true - assert.Equal("b2", resRem.Data[i].Custom["a2"]) - assert.Equal("d2", resRem.Data[i].Custom["c2"]) - assert.Equal(userid, resRem.Data[i].UUID.ID) - assert.Equal(name, resRem.Data[i].UUID.Name) - assert.Equal(extid, resRem.Data[i].UUID.ExternalID) - assert.Equal(purl, resRem.Data[i].UUID.ProfileURL) - assert.Equal(email, resRem.Data[i].UUID.Email) - assert.Equal(custom["a"], resRem.Data[i].UUID.Custom["a"]) - assert.Equal(custom["c"], resRem.Data[i].UUID.Custom["c"]) - - } - } - assert.False(foundRem) - } else { - if enableDebuggingInTests { - - fmt.Println("ManageMembers->", errRem.Error()) - } - } - - channel2 := pubnub.PNMembershipsChannel{ - ID: channelid2, - } - - inMem := pubnub.PNMembershipsSet{ - Channel: channel2, - Custom: custom3, - } - - channel3 := pubnub.PNMembershipsChannel{ - ID: channelid3, - } - - inMemChannel3 := pubnub.PNMembershipsSet{ - Channel: channel3, - Custom: custom3, - } - - inArrMem := []pubnub.PNMembershipsSet{ - inMem, - inMemChannel3, - } - - //Add user memberships - resManageMemAdd, stManageMemAdd, errManageMemAdd := pn.SetMemberships().UUID(userid2).Set(inArrMem).Include(inclMemberships).Limit(limit).Count(count).Execute() - //fmt.Println("resManageMemAdd -->", resManageMemAdd) - assert.Nil(errManageMemAdd) - assert.Equal(200, stManageMemAdd.StatusCode) - if errManageMemAdd == nil { - foundManageMembers := false - for i := range resManageMemAdd.Data { - if resManageMemAdd.Data[i].Channel.ID == channelid2 { - assert.Equal(channelid2, resManageMemAdd.Data[i].Channel.ID) - assert.Equal(name, resManageMemAdd.Data[i].Channel.Name) - assert.Equal(desc, resManageMemAdd.Data[i].Channel.Description) - assert.Equal(custom2["a1"], resManageMemAdd.Data[i].Channel.Custom["a1"]) - assert.Equal(custom2["c1"], resManageMemAdd.Data[i].Channel.Custom["c1"]) - assert.Equal(custom3["a3"], resManageMemAdd.Data[i].Custom["a3"]) - assert.Equal(custom3["c3"], resManageMemAdd.Data[i].Custom["c3"]) - foundManageMembers = true - } - } - assert.True(foundManageMembers) - } else { - if enableDebuggingInTests { - - fmt.Println("ManageMemberships->", errManageMemAdd.Error()) - } - } - - // //Update user memberships - - custom5 := make(map[string]interface{}) - custom5["a5"] = "b5" - custom5["c5"] = "d5" - - upMem := pubnub.PNMembershipsSet{ - Channel: channel2, - Custom: custom5, - } - - upArrMem := []pubnub.PNMembershipsSet{ - upMem, - } - sortMemberships1 := false - sortMemberships2 := false - - resManageMemUp, stManageMemUp, errManageMemUp := pn.SetMemberships().UUID(userid2).Sort(sort).Set(upArrMem).Include(inclMemberships).Limit(limit).Count(count).Execute() - //fmt.Println("resManageMemUp -->", resManageMemUp) - assert.Nil(errManageMemUp) - assert.Equal(200, stManageMemUp.StatusCode) - if errManageMemUp == nil { - foundManageMembersUp := false - - for i := range resManageMemUp.Data { - //fmt.Println("resManageMemUp.Data[i].ID == channelid2-->", resRem.Data[i].UUID.ID, channelid2) - if resManageMemUp.Data[i].Channel.ID == channelid2 { - assert.Equal(channelid2, resManageMemUp.Data[i].Channel.ID) - assert.Equal(name, resManageMemUp.Data[i].Channel.Name) - assert.Equal(desc, resManageMemUp.Data[i].Channel.Description) - assert.Equal(custom2["a1"], resManageMemAdd.Data[i].Channel.Custom["a1"]) - assert.Equal(custom2["c1"], resManageMemAdd.Data[i].Channel.Custom["c1"]) - assert.Equal(custom5["a5"], resManageMemUp.Data[i].Custom["a5"]) - assert.Equal(custom5["c5"], resManageMemUp.Data[i].Custom["c5"]) - foundManageMembersUp = true - } - } - if (resManageMemUp.Data != nil) && (len(resManageMemUp.Data) > 1) { - sortMemberships1 = (resManageMemUp.Data[0].Channel.ID == channelid2) - sortMemberships2 = (resManageMemUp.Data[1].Channel.ID == channelid3) - assert.True(sortMemberships1) - assert.True(sortMemberships2) - } else { - assert.Fail("Sort ", "resManageMemUp.Data null or ", len(resManageMemUp.Data)) - } - - assert.True(foundManageMembersUp) - } else { - if enableDebuggingInTests { - - fmt.Println("ManageMemberships->", errManageMemUp.Error()) - } - } - - // //Get members - resGetMembers, stGetMembers, errGetMembers := pn.GetChannelMembers().Channel(channelid2).Include(inclSm).Limit(limit).Count(count).Execute() - //fmt.Println("resGetMembers -->", resGetMembers) - assert.Nil(errGetMembers) - assert.Equal(200, stGetMembers.StatusCode) - if errGetMembers == nil { - foundGetMembers := false - for i := range resGetMembers.Data { - if resGetMembers.Data[i].UUID.ID == userid2 { - foundGetMembers = true - assert.Equal(name, resGetMembers.Data[i].UUID.Name) - assert.Equal(extid, resGetMembers.Data[i].UUID.ExternalID) - assert.Equal(purl, resGetMembers.Data[i].UUID.ProfileURL) - assert.Equal(email, resGetMembers.Data[i].UUID.Email) - assert.Equal(custom["a"], resGetMembers.Data[i].UUID.Custom["a"]) - assert.Equal(custom["c"], resGetMembers.Data[i].UUID.Custom["c"]) - assert.Equal(custom5["a5"], resGetMembers.Data[i].Custom["a5"]) - assert.Equal(custom5["c5"], resGetMembers.Data[i].Custom["c5"]) - } - } - - assert.True(foundGetMembers) - } else { - if enableDebuggingInTests { - - fmt.Println("GetMembers->", errGetMembers.Error()) - } - } - - //filterExp2 := fmt.Sprintf("custom.a5 == '%s' || custom.c5 == '%s'", custom5["a5"], custom5["c5"]) - filterExp2 := fmt.Sprintf("uuid.name == '%s'", name) - //fmt.Println("GetMembers ====>", filterExp2) - - resGetMembersF, stGetMembersF, errGetMembersF := pn.GetChannelMembers().Channel(channelid2).Include(inclSm).Filter(filterExp2).Limit(limit).Count(count).Execute() - //fmt.Println("resGetMembers -->", resGetMembersF) - assert.Nil(errGetMembersF) - assert.Equal(200, stGetMembersF.StatusCode) - if errGetMembersF == nil { - foundGetMembersF := false - - for i := range resGetMembersF.Data { - if resGetMembersF.Data[i].UUID.ID == userid2 { - foundGetMembersF = true - assert.Equal(name, resGetMembersF.Data[i].UUID.Name) - assert.Equal(extid, resGetMembersF.Data[i].UUID.ExternalID) - assert.Equal(purl, resGetMembersF.Data[i].UUID.ProfileURL) - assert.Equal(email, resGetMembersF.Data[i].UUID.Email) - assert.Equal(custom["a"], resGetMembersF.Data[i].UUID.Custom["a"]) - assert.Equal(custom["c"], resGetMembersF.Data[i].UUID.Custom["c"]) - assert.Equal(custom5["a5"], resGetMembersF.Data[i].Custom["a5"]) - assert.Equal(custom5["c5"], resGetMembersF.Data[i].Custom["c5"]) - } - } - assert.True(foundGetMembersF) - } else { - if enableDebuggingInTests { - - fmt.Println("GetMembers->", errGetMembersF.Error()) - } - } - - // //Remove user memberships - - reMem := pubnub.PNMembershipsRemove{ - Channel: channel2, - } - - reArrMem := []pubnub.PNMembershipsRemove{ - reMem, - } - resManageMemRem, stManageMemRem, errManageMemRem := pn.RemoveMemberships().UUID(userid2).Sort(sort).Remove(reArrMem).Include(inclMemberships).Limit(limit).Count(count).Execute() - assert.Nil(errManageMemRem) - assert.Equal(200, stManageMemRem.StatusCode) - if errManageMemRem == nil { - - foundManageMemRem := false - for i := range resManageMemRem.Data { - if resManageMemRem.Data[i].Channel.ID == channelid2 { - foundManageMemRem = true - } - } - assert.False(foundManageMemRem) - } else { - if enableDebuggingInTests { - - fmt.Println("ManageMemberships->", errManageMemRem.Error()) - } - } - - //delete - res5, st5, err5 := pn.RemoveUUIDMetadata().UUID(userid).Execute() - assert.Nil(err5) - assert.Equal(200, st5.StatusCode) - - assert.Nil(res5.Data) - - //delete - res6, st6, err6 := pn.RemoveChannelMetadata().Channel(channelid).Execute() - assert.Nil(err6) - assert.Equal(200, st6.StatusCode) - assert.Nil(res6.Data) - - //delete - res52, st52, err52 := pn.RemoveUUIDMetadata().UUID(userid2).Execute() - assert.Nil(err52) - assert.Equal(200, st52.StatusCode) - if res52 != nil { - assert.Nil(res52.Data) - } - - //delete - res62, st62, err62 := pn.RemoveChannelMetadata().Channel(channelid2).Execute() - assert.Nil(err62) - assert.Equal(200, st62.StatusCode) - if res62 != nil { - assert.Nil(res62.Data) - } - -} - -func TestObjectsV2MembershipsV2(t *testing.T) { - ObjectsMembershipsCommonV2(t, false, false) -} - -func TestObjectsV2MembershipsV2WithPAM(t *testing.T) { - ObjectsMembershipsCommonV2(t, true, false) } -// PASSES after adding PAM checks for Update Members -// func TestObjectsV2MembershipsV2WithPAMWithoutSecKey(t *testing.T) { -// ObjectsMembershipsCommonV2(t, true, true) -// } - -func ObjectsMembershipsCommonV2(t *testing.T, withPAM, runWithoutSecretKey bool) { - assert := assert.New(t) - - limit := 100 - count := true +func TestObjectsV2UUIDMetadataSetUpdateGetRemove(t *testing.T) { + a := assert.New(t) pn := pubnub.NewPubNub(configCopy()) - userid := randomized("testuser1") - channelid := randomized("testchannel1") - - if withPAM { - pn2 := ActivateWithPAMV2() - if runWithoutSecretKey { - tokens := RunGrantV2(pn2, []string{userid}, []string{channelid}, true, true, true, true, true, true) - SetPNV2(pn, pn2, tokens) - } else { - pn = pn2 - RunGrantV2(pn, []string{userid}, []string{channelid}, true, true, true, true, true, false) - } - - } + id := randomized("testuuid") if enableDebuggingInTests { pn.Config.Log = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) } name := randomized("name") - extid := "extid" - purl := "profileurl" - email := "email" - - custom := make(map[string]interface{}) - custom["a"] = "b" - custom["c"] = "d" + email := "go@pubnub.com" + custom := map[string]interface{}{"a": "b", "c": "d"} - inclUUID := []pubnub.PNUUIDMetadataInclude{ + incl := []pubnub.PNUUIDMetadataInclude{ pubnub.PNUUIDMetadataIncludeCustom, } - res, st, err := pn.SetUUIDMetadata().Include(inclUUID).UUID(userid).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(custom).Execute() - assert.Nil(err) - assert.Equal(200, st.StatusCode) - if res != nil { - assert.Equal(userid, res.Data.ID) - assert.Equal(name, res.Data.Name) - assert.Equal(extid, res.Data.ExternalID) - assert.Equal(purl, res.Data.ProfileURL) - assert.Equal(email, res.Data.Email) - // assert.NotNil(res.Data.Created) - assert.NotNil(res.Data.Updated) - assert.NotNil(res.Data.ETag) - assert.Equal("b", res.Data.Custom["a"]) - assert.Equal("d", res.Data.Custom["c"]) - } - - desc := "desc" - custom2 := make(map[string]interface{}) - custom2["a1"] = "b1" - custom2["c1"] = "d1" - - inclChannel := []pubnub.PNChannelMetadataInclude{ - pubnub.PNChannelMetadataIncludeCustom, - } - - checkFor(assert, time.Second, time.Millisecond*200, func() error { - res2, st2, err2 := pn.SetChannelMetadata().Include(inclChannel).Channel(channelid).Name(name).Description(desc).Custom(custom2).Execute() - if err2 != nil { - return err2 - } - if 200 != st2.StatusCode { - return fmt.Errorf("expected status code 200 but got %d", st2.StatusCode) - } - if res2 != nil { - if channelid != res2.Data.ID { - return fmt.Errorf("expected channelId %s but got %s", channelid, res2.Data.ID) - } - if name != res2.Data.Name { - return fmt.Errorf("expected name %s but got %s", name, res2.Data.Name) - } - if desc != res2.Data.Description { - return fmt.Errorf("expected desc %s but got %s", desc, res2.Data.Description) - } - //assert.NotNil(res2.Data.Updated) - //assert.NotNil(res2.Data.ETag) - if "b1" != res2.Data.Custom["a1"] { - return fmt.Errorf("expected custom a1 -> b1 but got a1 -> %v", res2.Data.Custom["a1"]) - } - if "d1" != res2.Data.Custom["c1"] { - return fmt.Errorf("expected custom a1 -> b1 but got a1 -> %v", res2.Data.Custom["a1"]) - } - return nil - } - return errors.New("expected res to be not nil") - }) - res2, st2, err2 := pn.SetChannelMetadata().Include(inclChannel).Channel(channelid).Name(name).Description(desc).Custom(custom2).Execute() - assert.Nil(err2) - assert.Equal(200, st2.StatusCode) - //fmt.Println("res2-->", res2) - if res2 != nil { - assert.Equal(channelid, res2.Data.ID) - assert.Equal(name, res2.Data.Name) - assert.Equal(desc, res2.Data.Description) - // assert.NotNil(res2.Data.Created) - assert.NotNil(res2.Data.Updated) - assert.NotNil(res2.Data.ETag) - assert.Equal("b1", res2.Data.Custom["a1"]) - assert.Equal("d1", res2.Data.Custom["c1"]) - } - - userid2 := randomized("testuser2") - - _, st3, err3 := pn.SetUUIDMetadata().Include(inclUUID).UUID(userid2).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(custom).Execute() - assert.Nil(err3) - assert.Equal(200, st3.StatusCode) - - channelid2 := randomized("testchannel2") - - _, st4, err4 := pn.SetChannelMetadata().Include(inclChannel).Channel(channelid2).Name(name).Description(desc).Custom(custom2).Execute() - assert.Nil(err4) - assert.Equal(200, st4.StatusCode) - - userid3 := randomized("testuser3") - - _, stuser3, erruser3 := pn.SetUUIDMetadata().Include(inclUUID).UUID(userid3).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(custom).Execute() - assert.Nil(erruser3) - assert.Equal(200, stuser3.StatusCode) - - channelid3 := randomized("testchannel3") - - _, stchannel3, errchannel3 := pn.SetChannelMetadata().Include(inclChannel).Channel(channelid3).Name(name).Description(desc).Custom(custom2).Execute() - assert.Nil(errchannel3) - assert.Equal(200, stchannel3.StatusCode) - - inclSm := []pubnub.PNChannelMembersInclude{ - pubnub.PNChannelMembersIncludeUUIDCustom, - pubnub.PNChannelMembersIncludeUUID, - pubnub.PNChannelMembersIncludeCustom, - } - - custom3 := make(map[string]interface{}) - custom3["a3"] = "b3" - custom3["c3"] = "d3" - - uuid := pubnub.PNChannelMembersUUID{ - ID: userid, - } - - in := pubnub.PNChannelMembersSet{ - UUID: uuid, - Custom: custom3, - } - uuid3 := pubnub.PNChannelMembersUUID{ - ID: userid3, - } - - inUser3 := pubnub.PNChannelMembersSet{ - UUID: uuid3, - Custom: custom3, - } - - inArr := []pubnub.PNChannelMembersSet{ - in, - inUser3, - } - - //Add Channel Memberships - sortManageChannelMembers := []string{"uuid.id:desc"} - sort := []string{"updated:desc"} - - resAdd, stAdd, errAdd := pn.ManageChannelMembers().Channel(channelid).Sort(sortManageChannelMembers).Set(inArr).Remove([]pubnub.PNChannelMembersRemove{}).Include(inclSm).Limit(limit).Count(count).Execute() - assert.Nil(errAdd) - assert.Equal(200, stAdd.StatusCode) - if errAdd == nil { - sortMembers1 := false - sortMembers2 := false - - found := false - assert.True(resAdd.TotalCount > 0) - //fmt.Println("resAdd-->", resAdd) - - for i := range resAdd.Data { - if resAdd.Data[i].UUID.ID == userid { - found = true - assert.Equal(custom3["a3"], resAdd.Data[i].Custom["a3"]) - assert.Equal(custom3["c3"], resAdd.Data[i].Custom["c3"]) - assert.Equal(userid, resAdd.Data[i].UUID.ID) - assert.Equal(name, resAdd.Data[i].UUID.Name) - assert.Equal(extid, resAdd.Data[i].UUID.ExternalID) - assert.Equal(purl, resAdd.Data[i].UUID.ProfileURL) - assert.Equal(email, resAdd.Data[i].UUID.Email) - assert.Equal(custom["a"], resAdd.Data[i].UUID.Custom["a"]) - assert.Equal(custom["c"], resAdd.Data[i].UUID.Custom["c"]) - } - } - if (resAdd.Data != nil) && (len(resAdd.Data) > 1) { - sortMembers1 = (resAdd.Data[1].UUID.ID == userid) - sortMembers2 = (resAdd.Data[0].UUID.ID == userid3) - assert.True(sortMembers1) - assert.True(sortMembers2) - } else { - assert.Fail("Sort ", "resAdd.Data null or ", len(resAdd.Data)) - } - - assert.True(found) - } else { - if enableDebuggingInTests { - - fmt.Println("ManageMembers->", errAdd.Error()) - } - } - - //Update Channel Memberships - if !withPAM { - - custom4 := make(map[string]interface{}) - custom4["a2"] = "b2" - custom4["c2"] = "d2" - - up := pubnub.PNChannelMembersSet{ - UUID: uuid, - Custom: custom4, - } - - upArr := []pubnub.PNChannelMembersSet{ - up, - } - - resUp, stUp, errUp := pn.ManageChannelMembers().Channel(channelid).Sort(sort).Set(upArr).Remove([]pubnub.PNChannelMembersRemove{}).Include(inclSm).Limit(limit).Count(count).Execute() - assert.Nil(errUp) - assert.Equal(200, stUp.StatusCode) - if errUp == nil { - assert.True(resUp.TotalCount > 0) - foundUp := false - for i := range resUp.Data { - if resUp.Data[i].UUID.ID == userid { - foundUp = true - assert.Equal("b2", resUp.Data[i].Custom["a2"]) - assert.Equal("d2", resUp.Data[i].Custom["c2"]) - //assert.Equal(userid, resAdd.Data[i].UUID.ID) - assert.Equal(name, resAdd.Data[i].UUID.Name) - assert.Equal(extid, resAdd.Data[i].UUID.ExternalID) - assert.Equal(purl, resAdd.Data[i].UUID.ProfileURL) - assert.Equal(email, resAdd.Data[i].UUID.Email) - assert.Equal(custom["a"], resAdd.Data[i].UUID.Custom["a"]) - assert.Equal(custom["c"], resAdd.Data[i].UUID.Custom["c"]) - - } - } - assert.True(foundUp) - } else { - if enableDebuggingInTests { - - fmt.Println("ManageMembers->", errUp.Error()) - } - } - } - //Get Channel Memberships - - inclMemberships := []pubnub.PNMembershipsInclude{ - pubnub.PNMembershipsIncludeCustom, - pubnub.PNMembershipsIncludeChannel, - pubnub.PNMembershipsIncludeChannelCustom, - } - - //fmt.Println("GetMemberships ====>") - - resGetMem, stGetMem, errGetMem := pn.GetMemberships().UUID(userid).Include(inclMemberships).Sort(sort).Limit(limit).Count(count).Execute() - foundGetMem := false - assert.Nil(errGetMem) - if errGetMem == nil { - for i := range resGetMem.Data { - if resGetMem.Data[i].Channel.ID == channelid { - foundGetMem = true - assert.Equal(name, resGetMem.Data[i].Channel.Name) - assert.Equal(desc, resGetMem.Data[i].Channel.Description) - assert.Equal("b1", resGetMem.Data[i].Channel.Custom["a1"]) - assert.Equal("d1", resGetMem.Data[i].Channel.Custom["c1"]) - if withPAM { - assert.Equal("b3", resGetMem.Data[i].Custom["a3"]) - assert.Equal("d3", resGetMem.Data[i].Custom["c3"]) - } else { - assert.Equal("b2", resGetMem.Data[i].Custom["a2"]) - assert.Equal("d2", resGetMem.Data[i].Custom["c2"]) - } - } - } - assert.Equal(200, stGetMem.StatusCode) - assert.True(foundGetMem) - } else { - if enableDebuggingInTests { - fmt.Println("GetMemberships->", errGetMem.Error()) - } - } - - //filterExp := fmt.Sprintf("custom.c3 == '%s' || custom.c2 == '%s'", "d3", "d2") - filterExp := fmt.Sprintf("channel.name == '%s'", name) - - //fmt.Println("GetMemberships ====>", filterExp) - - resGetMemF, stGetMemF, errGetMemF := pn.GetMemberships().UUID(userid).Include(inclMemberships).Filter(filterExp).Limit(limit).Count(count).Execute() - foundGetMemF := false - assert.Nil(errGetMemF) - if errGetMemF == nil { - for i := range resGetMemF.Data { - if resGetMemF.Data[i].Channel.ID == channelid { - foundGetMemF = true - assert.Equal(name, resGetMemF.Data[i].Channel.Name) - assert.Equal(desc, resGetMemF.Data[i].Channel.Description) - assert.Equal("b1", resGetMemF.Data[i].Channel.Custom["a1"]) - assert.Equal("d1", resGetMemF.Data[i].Channel.Custom["c1"]) - if withPAM { - assert.Equal("b3", resGetMemF.Data[i].Custom["a3"]) - assert.Equal("d3", resGetMemF.Data[i].Custom["c3"]) - } else { - assert.Equal("b2", resGetMemF.Data[i].Custom["a2"]) - assert.Equal("d2", resGetMemF.Data[i].Custom["c2"]) - } - } - } - assert.Equal(200, stGetMemF.StatusCode) - assert.True(foundGetMemF) - } else { - if enableDebuggingInTests { - - fmt.Println("GetMemberships->", errGetMemF.Error()) - } - } - - //Remove Channel Memberships - re := pubnub.PNChannelMembersRemove{ - UUID: uuid, - } - - reArr := []pubnub.PNChannelMembersRemove{ - re, - } - resRem, stRem, errRem := pn.ManageChannelMembers().Channel(channelid).Set([]pubnub.PNChannelMembersSet{}).Remove(reArr).Include(inclSm).Limit(limit).Count(count).Execute() - assert.Nil(errRem) - assert.Equal(200, stRem.StatusCode) - if errRem == nil { - - foundRem := false - for i := range resRem.Data { - if resRem.Data[i].UUID.ID == userid { - foundRem = true - assert.Equal("b2", resRem.Data[i].Custom["a2"]) - assert.Equal("d2", resRem.Data[i].Custom["c2"]) - assert.Equal(userid, resRem.Data[i].UUID.ID) - assert.Equal(name, resRem.Data[i].UUID.Name) - assert.Equal(extid, resRem.Data[i].UUID.ExternalID) - assert.Equal(purl, resRem.Data[i].UUID.ProfileURL) - assert.Equal(email, resRem.Data[i].UUID.Email) - assert.Equal(custom["a"], resRem.Data[i].UUID.Custom["a"]) - assert.Equal(custom["c"], resRem.Data[i].UUID.Custom["c"]) - - } - } - assert.False(foundRem) - } else { - if enableDebuggingInTests { - - fmt.Println("ManageMembers->", errRem.Error()) - } - } - - channel2 := pubnub.PNMembershipsChannel{ - ID: channelid2, - } - - inMem := pubnub.PNMembershipsSet{ - Channel: channel2, - Custom: custom3, - } - - channel3 := pubnub.PNMembershipsChannel{ - ID: channelid3, - } - - inMemChannel3 := pubnub.PNMembershipsSet{ - Channel: channel3, - Custom: custom3, - } - - inArrMem := []pubnub.PNMembershipsSet{ - inMem, - inMemChannel3, - } - - //Add user memberships - resManageMemAdd, stManageMemAdd, errManageMemAdd := pn.ManageMemberships().UUID(userid2).Set(inArrMem).Remove([]pubnub.PNMembershipsRemove{}).Include(inclMemberships).Limit(limit).Count(count).Execute() - //fmt.Println("resManageMemAdd -->", resManageMemAdd) - assert.Nil(errManageMemAdd) - assert.Equal(200, stManageMemAdd.StatusCode) - if errManageMemAdd == nil { - foundManageMembers := false - for i := range resManageMemAdd.Data { - if resManageMemAdd.Data[i].Channel.ID == channelid2 { - assert.Equal(channelid2, resManageMemAdd.Data[i].Channel.ID) - assert.Equal(name, resManageMemAdd.Data[i].Channel.Name) - assert.Equal(desc, resManageMemAdd.Data[i].Channel.Description) - assert.Equal(custom2["a1"], resManageMemAdd.Data[i].Channel.Custom["a1"]) - assert.Equal(custom2["c1"], resManageMemAdd.Data[i].Channel.Custom["c1"]) - assert.Equal(custom3["a3"], resManageMemAdd.Data[i].Custom["a3"]) - assert.Equal(custom3["c3"], resManageMemAdd.Data[i].Custom["c3"]) - foundManageMembers = true - } - } - assert.True(foundManageMembers) - } else { - if enableDebuggingInTests { + defer removeUUIDMetadata(a, pn, id) + res, st, err := pn.SetUUIDMetadata().Include(incl).UUID(id).Name(name).Email(email).Custom(custom).Execute() - fmt.Println("ManageMemberships->", errManageMemAdd.Error()) - } - } - - // //Update user memberships - - custom5 := make(map[string]interface{}) - custom5["a5"] = "b5" - custom5["c5"] = "d5" - - upMem := pubnub.PNMembershipsSet{ - Channel: channel2, - Custom: custom5, - } - - upArrMem := []pubnub.PNMembershipsSet{ - upMem, - } - sortMemberships1 := false - sortMemberships2 := false - - resManageMemUp, stManageMemUp, errManageMemUp := pn.ManageMemberships().UUID(userid2).Sort(sort).Set(upArrMem).Remove([]pubnub.PNMembershipsRemove{}).Include(inclMemberships).Limit(limit).Count(count).Execute() - //fmt.Println("resManageMemUp -->", resManageMemUp) - assert.Nil(errManageMemUp) - assert.Equal(200, stManageMemUp.StatusCode) - if errManageMemUp == nil { - foundManageMembersUp := false - - for i := range resManageMemUp.Data { - //fmt.Println("resManageMemUp.Data[i].ID == channelid2-->", resRem.Data[i].UUID.ID, channelid2) - if resManageMemUp.Data[i].Channel.ID == channelid2 { - assert.Equal(channelid2, resManageMemUp.Data[i].Channel.ID) - assert.Equal(name, resManageMemUp.Data[i].Channel.Name) - assert.Equal(desc, resManageMemUp.Data[i].Channel.Description) - assert.Equal(custom2["a1"], resManageMemAdd.Data[i].Channel.Custom["a1"]) - assert.Equal(custom2["c1"], resManageMemAdd.Data[i].Channel.Custom["c1"]) - assert.Equal(custom5["a5"], resManageMemUp.Data[i].Custom["a5"]) - assert.Equal(custom5["c5"], resManageMemUp.Data[i].Custom["c5"]) - foundManageMembersUp = true - } - } - if (resManageMemUp.Data != nil) && (len(resManageMemUp.Data) > 1) { - sortMemberships1 = (resManageMemUp.Data[0].Channel.ID == channelid2) - sortMemberships2 = (resManageMemUp.Data[1].Channel.ID == channelid3) - assert.True(sortMemberships1) - assert.True(sortMemberships2) - } else { - assert.Fail("Sort ", "resManageMemUp.Data null or ", len(resManageMemUp.Data)) - } - - assert.True(foundManageMembersUp) - } else { - if enableDebuggingInTests { - - fmt.Println("ManageMemberships->", errManageMemUp.Error()) - } - } - - // //Get members - resGetMembers, stGetMembers, errGetMembers := pn.GetChannelMembers().Channel(channelid2).Include(inclSm).Limit(limit).Count(count).Execute() - //fmt.Println("resGetMembers -->", resGetMembers) - assert.Nil(errGetMembers) - assert.Equal(200, stGetMembers.StatusCode) - if errGetMembers == nil { - foundGetMembers := false - for i := range resGetMembers.Data { - if resGetMembers.Data[i].UUID.ID == userid2 { - foundGetMembers = true - assert.Equal(name, resGetMembers.Data[i].UUID.Name) - assert.Equal(extid, resGetMembers.Data[i].UUID.ExternalID) - assert.Equal(purl, resGetMembers.Data[i].UUID.ProfileURL) - assert.Equal(email, resGetMembers.Data[i].UUID.Email) - assert.Equal(custom["a"], resGetMembers.Data[i].UUID.Custom["a"]) - assert.Equal(custom["c"], resGetMembers.Data[i].UUID.Custom["c"]) - assert.Equal(custom5["a5"], resGetMembers.Data[i].Custom["a5"]) - assert.Equal(custom5["c5"], resGetMembers.Data[i].Custom["c5"]) - } - } - - assert.True(foundGetMembers) - } else { - if enableDebuggingInTests { - - fmt.Println("GetMembers->", errGetMembers.Error()) - } - } - - //filterExp2 := fmt.Sprintf("custom.a5 == '%s' || custom.c5 == '%s'", custom5["a5"], custom5["c5"]) - filterExp2 := fmt.Sprintf("uuid.name == '%s'", name) - //fmt.Println("GetMembers ====>", filterExp2) - - resGetMembersF, stGetMembersF, errGetMembersF := pn.GetChannelMembers().Channel(channelid2).Include(inclSm).Filter(filterExp2).Limit(limit).Count(count).Execute() - //fmt.Println("resGetMembers -->", resGetMembersF) - assert.Nil(errGetMembersF) - assert.Equal(200, stGetMembersF.StatusCode) - if errGetMembersF == nil { - foundGetMembersF := false - - for i := range resGetMembersF.Data { - if resGetMembersF.Data[i].UUID.ID == userid2 { - foundGetMembersF = true - assert.Equal(name, resGetMembersF.Data[i].UUID.Name) - assert.Equal(extid, resGetMembersF.Data[i].UUID.ExternalID) - assert.Equal(purl, resGetMembersF.Data[i].UUID.ProfileURL) - assert.Equal(email, resGetMembersF.Data[i].UUID.Email) - assert.Equal(custom["a"], resGetMembersF.Data[i].UUID.Custom["a"]) - assert.Equal(custom["c"], resGetMembersF.Data[i].UUID.Custom["c"]) - assert.Equal(custom5["a5"], resGetMembersF.Data[i].Custom["a5"]) - assert.Equal(custom5["c5"], resGetMembersF.Data[i].Custom["c5"]) - } - } - assert.True(foundGetMembersF) - } else { - if enableDebuggingInTests { - - fmt.Println("GetMembers->", errGetMembersF.Error()) - } + a.Nil(err) + a.Equal(200, st.StatusCode) + if res != nil { + a.Equal(id, res.Data.ID) + a.Equal(name, res.Data.Name) + a.Equal(email, res.Data.Email) + a.NotNil(res.Data.Updated) + a.NotNil(res.Data.ETag) + a.True(reflect.DeepEqual(custom, res.Data.Custom)) } - // //Remove user memberships + email = "gosdk@pubnub.com" - reMem := pubnub.PNMembershipsRemove{ - Channel: channel2, + res, st, err = pn.SetUUIDMetadata().Include(incl).UUID(id).Name(name).Email(email).Custom(custom).Execute() + a.Nil(err) + a.Equal(200, st.StatusCode) + if res != nil { + a.Equal(id, res.Data.ID) + a.Equal(email, res.Data.Email) } - reArrMem := []pubnub.PNMembershipsRemove{ - reMem, - } - resManageMemRem, stManageMemRem, errManageMemRem := pn.ManageMemberships().UUID(userid2).Sort(sort).Set([]pubnub.PNMembershipsSet{}).Remove(reArrMem).Include(inclMemberships).Limit(limit).Count(count).Execute() - assert.Nil(errManageMemRem) - assert.Equal(200, stManageMemRem.StatusCode) - if errManageMemRem == nil { + _, st, err = pn.GetUUIDMetadata().Include(incl).UUID(id).Execute() + a.Nil(err) + a.Equal(200, st.StatusCode) - foundManageMemRem := false - for i := range resManageMemRem.Data { - if resManageMemRem.Data[i].Channel.ID == channelid2 { - foundManageMemRem = true - } - } - assert.False(foundManageMemRem) - } else { - if enableDebuggingInTests { +} - fmt.Println("ManageMemberships->", errManageMemRem.Error()) - } +func removeUUIDMetadata(a *assert.Assertions, pn *pubnub.PubNub, id string) { + res, st, err := pn.RemoveUUIDMetadata().UUID(id).Execute() + a.Nil(err) + a.Equal(200, st.StatusCode) + if res != nil { + a.Nil(res.Data) } +} - //delete - res5, st5, err5 := pn.RemoveUUIDMetadata().UUID(userid).Execute() - assert.Nil(err5) - assert.Equal(200, st5.StatusCode) - - assert.Nil(res5.Data) +func TestObjectsV2MembersAddRemove(t *testing.T) { + a := assert.New(t) - //delete - res6, st6, err6 := pn.RemoveChannelMetadata().Channel(channelid).Execute() - assert.Nil(err6) - assert.Equal(200, st6.StatusCode) - assert.Nil(res6.Data) + pn := pubnub.NewPubNub(configCopy()) + channelid := randomized("channel") + userid := randomized("uuid") + inc := []pubnub.PNChannelMembersInclude{pubnub.PNChannelMembersIncludeUUID} - //delete - res52, st52, err52 := pn.RemoveUUIDMetadata().UUID(userid2).Execute() - assert.Nil(err52) - assert.Equal(200, st52.StatusCode) - if res52 != nil { - assert.Nil(res52.Data) - } + defer removeChannelMembers(a, pn, channelid, userid) - //delete - res62, st62, err62 := pn.RemoveChannelMetadata().Channel(channelid2).Execute() - assert.Nil(err62) - assert.Equal(200, st62.StatusCode) - if res62 != nil { - assert.Nil(res62.Data) + res, st, err := pn. + SetChannelMembers(). + Channel(channelid). + Set([]pubnub.PNChannelMembersSet{{UUID: pubnub.PNChannelMembersUUID{ID: userid}}}). + Include(inc). + Execute() + a.Nil(err) + a.Equal(200, st.StatusCode) + if err == nil { + a.True(len(res.Data) > 0) } } -func TestObjectsV2ListenersV2(t *testing.T) { - ObjectsListenersCommonV2(t, false, false) -} - -func TestObjectsV2ListenersV2WithPAM(t *testing.T) { - ObjectsListenersCommonV2(t, true, false) +func removeChannelMembers(a *assert.Assertions, pn *pubnub.PubNub, channelid string, userid string) { + _, st, err := pn. + RemoveChannelMembers(). + Channel(channelid). + Remove([]pubnub.PNChannelMembersRemove{{UUID: pubnub.PNChannelMembersUUID{ID: userid}}}). + Execute() + a.Nil(err) + a.Equal(200, st.StatusCode) } -// func TestObjectsV2ListenersV2WithPAMWithoutSecKey(t *testing.T) { -// ObjectsListenersCommonV2(t, true, true) -// } - -func ObjectsListenersCommonV2(t *testing.T, withPAM, runWithoutSecretKey bool) { - //Create channel names for Channel and User - eventWaitTime := 2 - assert := assert.New(t) - - limit := 100 - count := true +func TestObjectsV2MembershipAddRemove(t *testing.T) { + a := assert.New(t) pn := pubnub.NewPubNub(configCopy()) - pnSub := pubnub.NewPubNub(configCopy()) - - userid := randomized("testlistuser") - channelid := randomized("testlistchannel") - if withPAM { - pn2 := ActivateWithPAMV2() - if runWithoutSecretKey { - if enableDebuggingInTests { - pn2.Config.Log = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) - } - tokens := RunGrantV2(pn2, []string{userid}, []string{channelid}, true, true, true, true, true, true) - SetPNV2(pn, pn2, tokens) - SetPNV2(pnSub, pn2, tokens) - //You have to use Grant v2 to subscribe - pnSub.Config.AuthKey = "authKey" - pn2.Grant(). - Read(true).Write(true).Manage(true). - Channels([]string{userid, channelid}). - AuthKeys([]string{pnSub.Config.AuthKey}). - Execute() - } else { - pn = pn2 - pnSub = pn2 - if enableDebuggingInTests { - pn.Config.Log = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) - } - RunGrantV2(pn, []string{userid}, []string{channelid}, true, true, true, true, true, false) - } - } - if enableDebuggingInTests { - pn.Config.Log = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) - pnSub.Config.Log = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) - } - - //Subscribe to the channel names - - listener := pubnub.NewListener() - - var mut sync.RWMutex - - addUserToChannel := false - addUserToChannel2 := false - updateUserMem := false - updateUser := false - updateChannel := false - removeUserFromChannel := false - deleteUser := false - deleteChannel := false - - doneConnected := make(chan bool) - exitListener := make(chan bool) - - go func() { - ExitLabel: - for { - //fmt.Println("Running =--->") - select { - - case status := <-listener.Status: - switch status.Category { - case pubnub.PNConnectedCategory: - doneConnected <- true - default: - if enableDebuggingInTests { - - fmt.Println(" --- status: ", status) - } - } - - case userEvent := <-listener.UUIDEvent: - if enableDebuggingInTests { - - fmt.Println(" --- UserEvent: ") - fmt.Println(fmt.Sprintf("%s", userEvent)) - fmt.Println(fmt.Sprintf("userEvent.Channel: %s", userEvent.Channel)) - fmt.Println(fmt.Sprintf("userEvent.SubscribedChannel: %s", userEvent.SubscribedChannel)) - fmt.Println(fmt.Sprintf("userEvent.Event: %s", userEvent.Event)) - fmt.Println(fmt.Sprintf("userEvent.UUID: %s", userEvent.UUID)) - fmt.Println(fmt.Sprintf("userEvent.Description: %s", userEvent.Description)) - fmt.Println(fmt.Sprintf("userEvent.Timestamp: %s", userEvent.Timestamp)) - fmt.Println(fmt.Sprintf("userEvent.Name: %s", userEvent.Name)) - fmt.Println(fmt.Sprintf("userEvent.ExternalID: %s", userEvent.ExternalID)) - fmt.Println(fmt.Sprintf("userEvent.ProfileURL: %s", userEvent.ProfileURL)) - fmt.Println(fmt.Sprintf("userEvent.Email: %s", userEvent.Email)) - // fmt.Println(fmt.Sprintf("userEvent.Created: %s", userEvent.Created)) - fmt.Println(fmt.Sprintf("userEvent.Updated: %s", userEvent.Updated)) - fmt.Println(fmt.Sprintf("userEvent.ETag: %s", userEvent.ETag)) - fmt.Println(fmt.Sprintf("userEvent.Custom: %v", userEvent.Custom)) - } - - if (userEvent.Event == pubnub.PNObjectsEventRemove) && (userEvent.UUID == userid) { - mut.Lock() - deleteUser = true - mut.Unlock() - } - if (userEvent.Event == pubnub.PNObjectsEventSet) && (userEvent.UUID == userid) { - mut.Lock() - updateUser = true - mut.Unlock() - } - case channelEvent := <-listener.ChannelEvent: - - if enableDebuggingInTests { - - fmt.Println(" --- ChannelEvent: ") - fmt.Println(fmt.Sprintf("%s", channelEvent)) - fmt.Println(fmt.Sprintf("channelEvent.SubscribedChannel: %s", channelEvent.SubscribedChannel)) - fmt.Println(fmt.Sprintf("channelEvent.Event: %s", channelEvent.Event)) - fmt.Println(fmt.Sprintf("channelEvent.ChannelID: %s", channelEvent.ChannelID)) - fmt.Println(fmt.Sprintf("channelEvent.Channel: %s", channelEvent.Channel)) - fmt.Println(fmt.Sprintf("channelEvent.Description: %s", channelEvent.Description)) - fmt.Println(fmt.Sprintf("channelEvent.Timestamp: %s", channelEvent.Timestamp)) - // fmt.Println(fmt.Sprintf("channelEvent.Created: %s", channelEvent.Created)) - fmt.Println(fmt.Sprintf("channelEvent.Updated: %s", channelEvent.Updated)) - fmt.Println(fmt.Sprintf("channelEvent.ETag: %s", channelEvent.ETag)) - fmt.Println(fmt.Sprintf("channelEvent.Custom: %v", channelEvent.Custom)) - } - if (channelEvent.Event == pubnub.PNObjectsEventRemove) && (channelEvent.ChannelID == channelid) { - mut.Lock() - deleteChannel = true - mut.Unlock() - } - if (channelEvent.Event == pubnub.PNObjectsEventSet) && (channelEvent.ChannelID == channelid) { - mut.Lock() - updateChannel = true - mut.Unlock() - } - - case membershipEvent := <-listener.MembershipEvent: - if enableDebuggingInTests { - - fmt.Println(" --- MembershipEvent: ") - fmt.Println(fmt.Sprintf("%s", membershipEvent)) - fmt.Println(fmt.Sprintf("membershipEvent.SubscribedChannel: %s", membershipEvent.SubscribedChannel)) - fmt.Println(fmt.Sprintf("membershipEvent.Event: %s", membershipEvent.Event)) - fmt.Println(fmt.Sprintf("membershipEvent.Channel: %s", membershipEvent.Channel)) - fmt.Println(fmt.Sprintf("membershipEvent.UUID: %s", membershipEvent.UUID)) - fmt.Println(fmt.Sprintf("membershipEvent.ChannelID: %s", membershipEvent.ChannelID)) - fmt.Println(fmt.Sprintf("membershipEvent.Description: %s", membershipEvent.Description)) - fmt.Println(fmt.Sprintf("membershipEvent.Timestamp: %s", membershipEvent.Timestamp)) - fmt.Println(fmt.Sprintf("membershipEvent.Custom: %v", membershipEvent.Custom)) - } - if (membershipEvent.Event == pubnub.PNObjectsEventSet) && (membershipEvent.ChannelID == channelid) && (membershipEvent.UUID == userid) && ((membershipEvent.Channel == channelid) || (membershipEvent.Channel == userid)) { - mut.Lock() - addUserToChannel = true - mut.Unlock() - } - if (membershipEvent.Event == pubnub.PNObjectsEventSet) && (membershipEvent.ChannelID == channelid) && (membershipEvent.UUID == userid) && ((membershipEvent.Channel == channelid) || (membershipEvent.Channel == userid)) { - mut.Lock() - addUserToChannel2 = true - mut.Unlock() - } - if (membershipEvent.Event == pubnub.PNObjectsEventSet) && (membershipEvent.ChannelID == channelid) && (membershipEvent.UUID == userid) && ((membershipEvent.Channel == channelid) || (membershipEvent.Channel == userid)) { - mut.Lock() - updateUserMem = true - mut.Unlock() - } - if (membershipEvent.Event == pubnub.PNObjectsEventSet) && (membershipEvent.ChannelID == channelid) && (membershipEvent.UUID == userid) && ((membershipEvent.Channel == channelid) || (membershipEvent.Channel == userid)) { - mut.Lock() - updateUserMem = true - mut.Unlock() - } - if (membershipEvent.Event == pubnub.PNObjectsEventRemove) && (membershipEvent.ChannelID == channelid) && (membershipEvent.UUID == userid) && ((membershipEvent.Channel == channelid) || (membershipEvent.Channel == userid)) { - mut.Lock() - removeUserFromChannel = true - mut.Unlock() - } - case <-exitListener: - break ExitLabel - - } - - //fmt.Println("=>>>>>>>>>>>>> restart") - - } - - }() - - pnSub.AddListener(listener) - - pnSub.Subscribe().Channels([]string{userid, channelid}).Execute() - tic := time.NewTicker(time.Duration(eventWaitTime) * time.Second) - select { - case <-doneConnected: - case <-tic.C: - tic.Stop() - assert.Fail("timeout") - } - - name := "name" - extid := "extid" - purl := "profileurl" - email := "email" - desc := "desc" - - customUser := make(map[string]interface{}) - customUser["au"] = "bu" - customUser["cu"] = "du" - - inclUUID := []pubnub.PNUUIDMetadataInclude{ - pubnub.PNUUIDMetadataIncludeCustom, - } - - //Create User - _, st, err := pn.SetUUIDMetadata().Include(inclUUID).UUID(userid).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(customUser).Execute() - assert.Nil(err) - assert.Equal(200, st.StatusCode) - - //Create Channel - customChannel := make(map[string]interface{}) - customChannel["as"] = "bs" - customChannel["cs"] = "ds" - - inclChannel := []pubnub.PNChannelMetadataInclude{ - pubnub.PNChannelMetadataIncludeCustom, - } - - _, st4, err4 := pn.SetChannelMetadata().Include(inclChannel).Channel(channelid).Name(name).Description(desc).Custom(customChannel).Execute() - assert.Nil(err4) - assert.Equal(200, st4.StatusCode) - - time.Sleep(1 * time.Second) - - //Update User - email = "email2" - //fmt.Println("SetUUIDMetadata, Update ===> ", userid) - - _, st2, err2 := pn.SetUUIDMetadata().Include(inclUUID).UUID(userid).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(customUser).Execute() - assert.Nil(err2) - assert.Equal(200, st2.StatusCode) - - time.Sleep(1 * time.Second) - mut.Lock() - assert.True(updateUser) - mut.Unlock() - - desc = "desc2" - - //fmt.Println("SetChannelMetadata, Update ===> ", channelid) - - //Update Channel - _, st3, err3 := pn.SetChannelMetadata().Include(inclChannel).Channel(channelid).Name(name).Description(desc).Custom(customChannel).Execute() - assert.Nil(err3) - assert.Equal(200, st3.StatusCode) - - time.Sleep(1 * time.Second) - mut.Lock() - assert.True(updateChannel) - mut.Unlock() - - //Add user to channel - inclSm := []pubnub.PNChannelMembersInclude{ - pubnub.PNChannelMembersIncludeCustom, - pubnub.PNChannelMembersIncludeUUID, - pubnub.PNChannelMembersIncludeUUIDCustom, - } - - if enableDebuggingInTests { - - fmt.Println("inclSm===>", inclSm) - for k, value := range inclSm { - fmt.Println("inclSm===>", k, value) - } - } - - custom3 := make(map[string]interface{}) - custom3["a3"] = "b3" - custom3["c3"] = "d3" + channelid := randomized("channel") + userid := randomized("uuid") + inc := []pubnub.PNMembershipsInclude{pubnub.PNMembershipsIncludeChannel} - uuid := pubnub.PNChannelMembersUUID{ - ID: userid, - } - - in := pubnub.PNChannelMembersSet{ - UUID: uuid, - Custom: custom3, - } + defer removeMemberships(a, pn, channelid, userid) - inArr := []pubnub.PNChannelMembersSet{ - in, - } - - _, stAdd, errAdd := pn.ManageChannelMembers().Channel(channelid).Set(inArr).Remove([]pubnub.PNChannelMembersRemove{}).Include(inclSm).Limit(limit).Count(count).Execute() - assert.Nil(errAdd) - if enableDebuggingInTests { - - if errAdd != nil { - fmt.Println("ManageMembers-->", errAdd) - } - } - assert.Equal(200, stAdd.StatusCode) - - time.Sleep(1 * time.Second) - mut.Lock() - assert.True(addUserToChannel && addUserToChannel2) - mut.Unlock() - - //Update user membership - - //Read event - - custom5 := make(map[string]interface{}) - custom5["a5"] = "b5" - custom5["c5"] = "d5" - - channel := pubnub.PNMembershipsChannel{ - ID: channelid, - } - - upMem := pubnub.PNMembershipsSet{ - Channel: channel, - Custom: custom5, - } - - upArrMem := []pubnub.PNMembershipsSet{ - upMem, - } - - inclMemberships := []pubnub.PNMembershipsInclude{ - pubnub.PNMembershipsIncludeCustom, - pubnub.PNMembershipsIncludeChannel, - pubnub.PNMembershipsIncludeChannelCustom, - } - - resManageMemUp, stManageMemUp, errManageMemUp := pn.ManageMemberships().UUID(userid).Set(upArrMem).Remove([]pubnub.PNMembershipsRemove{}).Include(inclMemberships).Limit(limit).Count(count).Execute() - - assert.Nil(errManageMemUp) - if enableDebuggingInTests { - - fmt.Println("resManageMemUp -->", resManageMemUp) - if errManageMemUp != nil { - fmt.Println("ManageMemberships-->", errManageMemUp) - } - } - assert.Equal(200, stManageMemUp.StatusCode) - - time.Sleep(1 * time.Second) - mut.Lock() - assert.True(updateUserMem) - mut.Unlock() - - //Remove user from channel - reMem := pubnub.PNMembershipsRemove{ - Channel: channel, - } - - reArrMem := []pubnub.PNMembershipsRemove{ - reMem, - } - _, stManageMemRem, errManageMemRem := pn.ManageMemberships().UUID(userid).Set([]pubnub.PNMembershipsSet{}).Remove(reArrMem).Include(inclMemberships).Limit(limit).Count(count).Execute() - assert.Nil(errManageMemRem) - if enableDebuggingInTests { - - if errManageMemRem != nil { - fmt.Println("ManageMemberships-->", errManageMemRem) - } + res, st, err := pn. + SetMemberships(). + UUID(userid). + Set([]pubnub.PNMembershipsSet{{Channel: pubnub.PNMembershipsChannel{ID: channelid}}}). + Include(inc). + Execute() + a.Nil(err) + a.Equal(200, st.StatusCode) + if err == nil { + a.True(len(res.Data) > 0) } - assert.Equal(200, stManageMemRem.StatusCode) - - time.Sleep(1 * time.Second) - mut.Lock() - assert.True(removeUserFromChannel) - mut.Unlock() - - //Delete user - res52, st52, err52 := pn.RemoveUUIDMetadata().UUID(userid).Execute() - assert.Nil(err52) - assert.Equal(200, st52.StatusCode) - assert.Nil(res52.Data) - - time.Sleep(1 * time.Second) - mut.Lock() - assert.True(deleteUser) - mut.Unlock() - //Delete Channel - res62, st62, err62 := pn.RemoveChannelMetadata().Channel(channelid).Execute() - assert.Nil(err62) - assert.Equal(200, st62.StatusCode) - assert.Nil(res62.Data) - - time.Sleep(1 * time.Second) - mut.Lock() - assert.True(deleteChannel) - mut.Unlock() - - exitListener <- true } -func Test_CleanUpTestEnv(t *testing.T) { - pn := pubnub.NewPubNub(configCopy()) - execute, _, err := pn.GetAllChannelMetadata().Execute() - if err != nil { - t.Error(err.Error()) - return - } - - fmt.Printf("%v", execute) +func removeMemberships(a *assert.Assertions, pn *pubnub.PubNub, channelid string, userid string) { + _, st, err := pn. + RemoveMemberships(). + UUID(userid). + Remove([]pubnub.PNMembershipsRemove{{Channel: pubnub.PNMembershipsChannel{ID: channelid}}}). + Execute() + a.Nil(err) + a.Equal(200, st.StatusCode) } From 171561848de52566ee0083be4f53c95b22b5980f Mon Sep 17 00:00:00 2001 From: PubNub Release Bot <120067856+pubnub-release-bot@users.noreply.github.com> Date: Mon, 16 Oct 2023 14:42:18 +0000 Subject: [PATCH 30/30] PubNub SDK v7.2.0 release. --- .pubnub.yml | 11 +++++++++-- CHANGELOG.md | 9 +++++++++ pubnub.go | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.pubnub.yml b/.pubnub.yml index c0647476..4d9ddc67 100644 --- a/.pubnub.yml +++ b/.pubnub.yml @@ -1,6 +1,13 @@ --- -version: v7.1.2 +version: v7.2.0 changelog: + - date: 2023-10-16 + version: v7.2.0 + changes: + - type: feature + text: "Update the crypto module structure and add enhanced AES-CBC cryptor." + - type: bug + text: "Improved security of crypto implementation by increasing the cipher key entropy by a factor of two." - date: 2023-05-11 version: v7.1.2 changes: @@ -733,7 +740,7 @@ sdks: distribution-type: package distribution-repository: GitHub package-name: Go - location: https://github.com/pubnub/go/releases/tag/v7.1.2 + location: https://github.com/pubnub/go/releases/tag/v7.2.0 requires: - name: "Go" diff --git a/CHANGELOG.md b/CHANGELOG.md index beed4af2..074c9bd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## v7.2.0 +October 16 2023 + +#### Added +- Update the crypto module structure and add enhanced AES-CBC cryptor. + +#### Fixed +- Improved security of crypto implementation by increasing the cipher key entropy by a factor of two. + ## v7.1.2 May 11 2023 diff --git a/pubnub.go b/pubnub.go index 43216b95..e3e3f8ab 100644 --- a/pubnub.go +++ b/pubnub.go @@ -15,7 +15,7 @@ import ( // Default constants const ( // Version :the version of the SDK - Version = "7.1.2" + Version = "7.2.0" // MaxSequence for publish messages MaxSequence = 65535 )