Repository: ably/ably-go
Pull request #334 (2021-06-04 06:11:12)

Unexport CipherParams.IV, useful only for tests
Taking IV as a parameter is not just useless for production code, it can
easily become a security vulnerability.

See #330.


Patch:
diff --git a/ably/proto_crypto.go b/ably/proto_crypto.go
index 45b350b..60c15dd 100644
--- a/ably/proto_crypto.go
+++ b/ably/proto_crypto.go
@@ -65,19 +65,15 @@ type CipherParams struct {
 	KeyLength int // Spec item (TZ2b)
 	// This is the private key used to  encrypt/decrypt payloads.
 	Key []byte // Spec item (TZ2d)
-
-	// This is a sequence of bytes used as initialization vector.This is used to
-	// user distinct ciphertexts are produced even when the same plaintext is
-	// encrypted multiple times independently with the same key.
-	//
-	// This field is optional. A random value will be generated if this is set to
-	// nil.
-	IV []byte
-
 	// The cipher mode to be used for encryption default is CBC.
 	//
 	// Spec item (TZ2c)
 	Mode CipherMode
+
+	// iv is the initialization vector. Used only for comparing resulting
+	// ciphertext with test fixtures; production code should always use a random
+	// unique IV per encrypted message.
+	iv []byte
 }
 
 // GetCipher returns a ChannelCipher based on the algorithms set in the
@@ -148,7 +144,7 @@ func (c *cbcCipher) Encrypt(plainText []byte) ([]byte, error) {
 		}
 		plainText = data
 	}
-	iv := c.params.IV
+	iv := c.params.iv
 	if iv == nil {
 		iv = make([]byte, aes.BlockSize)
 		if _, err = io.ReadFull(rand.Reader, iv); err != nil {


Test patch:
diff --git a/ably/export_test.go b/ably/export_test.go
index 72d2c71..60af408 100644
--- a/ably/export_test.go
+++ b/ably/export_test.go
@@ -255,3 +255,7 @@ func ChannelModeToFlag(mode ChannelMode) ProtoFlag {
 func DialWebsocket(proto string, u *url.URL, timeout time.Duration) (Conn, error) {
 	return dialWebsocket(proto, u, timeout)
 }
+
+func (p *CipherParams) SetIV(iv []byte) {
+	p.iv = iv
+}
diff --git a/ably/proto_message_test.go b/ably/proto_message_test.go
index e275187..c205e4f 100644
--- a/ably/proto_message_test.go
+++ b/ably/proto_message_test.go
@@ -25,13 +25,14 @@ func TestMessage(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
+	params := ably.CipherParams{
+		Key:       key,
+		KeyLength: 128,
+		Algorithm: ably.CipherAES,
+	}
+	params.SetIV(iv)
 	opts := &ably.ProtoChannelOptions{
-		Cipher: ably.CipherParams{
-			Key:       key,
-			KeyLength: 128,
-			IV:        iv,
-			Algorithm: ably.CipherAES,
-		},
+		Cipher: params,
 	}
 	sample := []struct {
 		desc        string
@@ -138,12 +139,13 @@ func TestMessage_CryptoDataFixtures_RSL6a1_RSL5b_RSL5c(t *testing.T) {
 			if err != nil {
 				t.Fatal(err)
 			}
+			params := ably.CipherParams{
+				Algorithm: ably.CipherAES,
+				Key:       key,
+			}
+			params.SetIV(iv)
 			opts := &ably.ProtoChannelOptions{
-				Cipher: ably.CipherParams{
-					Algorithm: ably.CipherAES,
-					Key:       key,
-					IV:        iv,
-				},
+				Cipher: params,
 			}
 			t.Run("fixture encode", func(t *testing.T) {
 				for _, item := range test.Items {
diff --git a/ably/rest_channel_test.go b/ably/rest_channel_test.go
index 5015309..93a97ef 100644
--- a/ably/rest_channel_test.go
+++ b/ably/rest_channel_test.go
@@ -119,12 +119,13 @@ func TestRESTChannel(t *testing.T) {
 		if err != nil {
 			t.Fatal(err)
 		}
-		opts := []ably.ChannelOption{ably.ChannelWithCipher(ably.CipherParams{
+		cipher := ably.CipherParams{
 			Key:       key,
 			KeyLength: 128,
-			IV:        iv,
 			Algorithm: ably.CipherAES,
-		})}
+		}
+		cipher.SetIV(iv)
+		opts := []ably.ChannelOption{ably.ChannelWithCipher(cipher)}
 		channelName := "encrypted_channel"
 		channel := client.Channels.Get(channelName, opts...)
 		sample := []struct {