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 <[email protected]>
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)