You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by ze...@apache.org on 2026/08/05 17:33:02 UTC

(arrow-go) branch main updated: fix(parquet): propagate nonce generation failures (#1045)

This is an automated email from the ASF dual-hosted git repository.

zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 7ac7d2f9 fix(parquet): propagate nonce generation failures (#1045)
7ac7d2f9 is described below

commit 7ac7d2f94fa00dea4ba81d0aa9635115d5ae30f2
Author: Minh Vu <vu...@gmail.com>
AuthorDate: Wed Aug 5 19:32:56 2026 +0200

    fix(parquet): propagate nonce generation failures (#1045)
    
    ## What changed
    
    Check the secure-random read used to generate Parquet AES nonces and
    stop encryption if nonce generation fails.
    
    ## Why
    
    The encryptor previously ignored the result of `crypto/rand.Read`. On Go
    versions where that function returns an error, encryption could continue
    with an all-zero or partially filled nonce. Reusing a nonce undermines
    the security guarantees of both AES-GCM and AES-CTR.
    
    The test uses a package-local random-read seam to inject an entropy
    failure without replacing the process-wide random reader. It covers both
    supported cipher modes.
    
    ## Validation
    
    `go test ./parquet/internal/encryption`
---
 parquet/internal/encryption/aes.go      |  6 +++++-
 parquet/internal/encryption/aes_test.go | 30 ++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/parquet/internal/encryption/aes.go b/parquet/internal/encryption/aes.go
index e6bed554..327d24d5 100644
--- a/parquet/internal/encryption/aes.go
+++ b/parquet/internal/encryption/aes.go
@@ -43,6 +43,8 @@ const (
 	bufferSizeLength = 4
 )
 
+var readRandom = rand.Read
+
 // Module constants for constructing the AAD bytes, the order here is
 // important as the constants are set via iota.
 const (
@@ -125,7 +127,9 @@ func (a *aesEncryptor) Encrypt(w io.Writer, src, key, aad []byte) int {
 	}
 
 	nonce := make([]byte, NonceLength)
-	rand.Read(nonce)
+	if _, err := readRandom(nonce); err != nil {
+		panic(fmt.Errorf("parquet: failed to generate encryption nonce: %w", err))
+	}
 
 	if a.mode == gcmMode {
 		aead, err := cipher.NewGCM(block)
diff --git a/parquet/internal/encryption/aes_test.go b/parquet/internal/encryption/aes_test.go
index f36f0e80..712d1b90 100644
--- a/parquet/internal/encryption/aes_test.go
+++ b/parquet/internal/encryption/aes_test.go
@@ -19,12 +19,42 @@ package encryption
 import (
 	"bytes"
 	"encoding/binary"
+	"errors"
 	"testing"
 
 	"github.com/apache/arrow-go/v18/parquet"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
+type failingRandomReader struct {
+	err error
+}
+
+func (r failingRandomReader) Read([]byte) (int, error) { return 0, r.err }
+
+func TestAESEncryptRejectsNonceGenerationFailure(t *testing.T) {
+	originalReadRandom := readRandom
+	t.Cleanup(func() { readRandom = originalReadRandom })
+	failingReader := failingRandomReader{err: errors.New("entropy unavailable")}
+	readRandom = failingReader.Read
+
+	for _, tc := range []struct {
+		name string
+		alg  parquet.Cipher
+	}{
+		{name: "GCM", alg: parquet.AesGcm},
+		{name: "CTR", alg: parquet.AesCtr},
+	} {
+		t.Run(tc.name, func(t *testing.T) {
+			encryptor := NewAesEncryptor(tc.alg, false)
+			require.PanicsWithError(t,
+				"parquet: failed to generate encryption nonce: entropy unavailable",
+				func() { encryptor.Encrypt(&bytes.Buffer{}, []byte("data"), make([]byte, 16), nil) })
+		})
+	}
+}
+
 func TestAESDecryptRejectsMalformedCiphertext(t *testing.T) {
 	decryptor := newAesDecryptor(parquet.AesGcm, false)
 	key := make([]byte, 16)