Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New crypto implementation #155

Merged
merged 30 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
17a8892
New crypto wip
kleewho Sep 1, 2023
3ce91e7
Stuck on the how and when to put Header into Writer in EncryptStream
kleewho Sep 7, 2023
2958f30
Implementation + property based tests of reader enc/dec
kleewho Sep 13, 2023
35829b8
Some contract tests are working
kleewho Sep 14, 2023
37a1ef2
Reduce number of layers
kleewho Sep 15, 2023
e2f48e1
Fix old tests
kleewho Sep 15, 2023
5a9ce3f
Check for lenght shorter than sentinel
kleewho Sep 15, 2023
10d198f
Use new crypto module in pubnub
kleewho Sep 15, 2023
cc43c10
Fix method calls
kleewho Sep 15, 2023
6af0c16
Support first mutation of config.CipherKey
kleewho Sep 18, 2023
555db45
Fixes to contract tests
kleewho Sep 18, 2023
2f6b5fc
Remove unneeded step
kleewho Sep 18, 2023
95f655f
Hopefully now
kleewho Sep 18, 2023
abc1251
Stupid stupid stupid
kleewho Sep 18, 2023
a1b0e73
Demodemodemodemodemodemodemodemo
kleewho Sep 18, 2023
0a4e162
Fixes
kleewho Sep 18, 2023
77158c6
Make e2e tests easier to run
kleewho Sep 18, 2023
0c7027a
Apply suggestions from code review
kleewho Sep 20, 2023
9cbfda0
After review changes
kleewho Sep 20, 2023
4ef0fec
After review changes. Next part
kleewho Sep 21, 2023
969ddef
Fix history_request_test.go file
kleewho Sep 21, 2023
dcb5113
Fix it better ;)
kleewho Sep 22, 2023
1d0e989
Maybe?
kleewho Sep 22, 2023
3a5f86c
Remove deprecation
kleewho Sep 28, 2023
654155e
Put correct cryptor data size if it's larger than 254
kleewho Sep 29, 2023
108c460
Add proper info in Deprecation of UseRandomInitializationVector
kleewho Oct 2, 2023
800988b
Include empty data checks
kleewho Oct 2, 2023
91718de
Fix fire request
kleewho Oct 2, 2023
a327a26
Simplify the objectsV2 tests significantly
kleewho Oct 3, 2023
1715618
PubNub SDK v7.2.0 release.
pubnub-release-bot Oct 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 26 additions & 24 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pubnub

import (
"fmt"
"github.com/pubnub/go/v7/crypto"
"log"
"sync"
)
Expand All @@ -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.
Expand Down Expand Up @@ -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))
}
Expand Down
91 changes: 91 additions & 0 deletions crypto/aes_cbc_cryptor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package crypto

import (
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"errors"
"fmt"
"io"
)

type aesCbcCryptor struct {
block cipher.Block
}

func NewAesCbcCryptor(cipherKey string) (ExtendedCryptor, error) {
block, e := aesCipher(cipherKey)
if e != nil {
return nil, e
}

return &aesCbcCryptor{
block: block,
}, nil
}

var crivId = "ACRH"

func (c *aesCbcCryptor) Id() string {
return crivId
}

func (c *aesCbcCryptor) 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 *aesCbcCryptor) 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
}

func (c *aesCbcCryptor) EncryptStream(reader io.Reader) (*EncryptedStreamData, error) {
iv := generateIV(aes.BlockSize)

return &EncryptedStreamData{
Metadata: iv,
Reader: newBlockModeEncryptingReader(reader, cipher.NewCBCEncrypter(c.block, iv)),
}, nil
}

func (c *aesCbcCryptor) 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
}

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
}
77 changes: 77 additions & 0 deletions crypto/aes_cbc_cryptor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package crypto

import (
"bytes"
"io"
"testing"
"testing/quick"
)

var defaultPropertyTestConfig = &quick.Config{
MaxCount: 1000,
}

func canDecryptEncryptStreamResult(in []byte) bool {
cryptor, e := NewAesCbcCryptor("enigma")
if e != nil {
return false
}

output, err := cryptor.EncryptStream(bytes.NewReader(in))
if err != nil {
return false
}

encrData, err := io.ReadAll(output.Reader)

decrypted, err := cryptor.Decrypt(&EncryptedData{
Data: encrData,
Metadata: output.Metadata,
})
if err != nil {
return false
}
return bytes.Equal(in, decrypted)
}

func canDecryptStreamEncryptResult(in []byte) bool {
cryptor, e := NewAesCbcCryptor("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) {
if err := quick.Check(canDecryptEncryptStreamResult, defaultPropertyTestConfig); err != nil {
t.Error(err)
}
}

func Test_AesCBC_DecryptStream(t *testing.T) {
if err := quick.Check(canDecryptStreamEncryptResult, defaultPropertyTestConfig); err != nil {
t.Error(err)
}
}
25 changes: 25 additions & 0 deletions crypto/cryptor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package crypto

import "io"

type EncryptedData struct {
Metadata []byte
Data []byte
}

type Cryptor interface {
Id() string
Encrypt(message []byte) (*EncryptedData, error)
Decrypt(encryptedData *EncryptedData) ([]byte, error)
}

type EncryptedStreamData struct {
Metadata []byte
Reader io.Reader
}

type ExtendedCryptor interface {
Cryptor
EncryptStream(reader io.Reader) (*EncryptedStreamData, error)
DecryptStream(encryptedData *EncryptedStreamData) (io.Reader, error)
}
Comment on lines +21 to +25
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that extended interface. Imo it works much better than having a big one cryptor - especially that file crypto is something a little bit different than raw data.

Loading
Loading