hectar-glitches commented on code in PR #1620:
URL: https://github.com/apache/iceberg-go/pull/1620#discussion_r3707729221


##########
encryption/standard_manager.go:
##########
@@ -0,0 +1,488 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package encryption
+
+import (
+       "context"
+       "crypto/aes"
+       "crypto/cipher"
+       "crypto/rand"
+       "encoding/binary"
+       "encoding/json"
+       "errors"
+       "fmt"
+       "io"
+       "io/fs"
+
+       icebergio "github.com/apache/iceberg-go/io"
+)
+
+// Defaults for [StandardEncryptionManager].
+const (
+       // StandardDefaultDEKLength is the default length, in bytes, of the
+       // per-file data encryption key (DEK) generated for AES-256-GCM.
+       StandardDefaultDEKLength = 32
+
+       // StandardDefaultBlockSize is the default plaintext block size, in
+       // bytes, used to split a file into independently authenticated AES-GCM
+       // blocks. Blocks allow random access (Seek/ReadAt) without buffering or
+       // decrypting the whole file.
+       StandardDefaultBlockSize = 64 * 1024
+)
+
+// Sentinel errors returned by [StandardEncryptionManager].
+var (
+       // ErrKeyIDRequired is returned by
+       // [StandardEncryptionManager.NewEncryptedOutputFile] when keyID is 
empty.
+       // StandardEncryptionManager always encrypts, so it requires a KEK to 
wrap
+       // the generated DEK; use [PlaintextEncryptionManager] for unencrypted
+       // tables instead of passing an empty keyID here.
+       ErrKeyIDRequired = errors.New("encryption: StandardEncryptionManager 
requires a non-empty keyID")
+
+       // ErrKeyMetadataRequired is returned by
+       // [StandardEncryptionManager.NewDecryptedInputFile] when keyMetadata is
+       // empty. StandardEncryptionManager always decrypts, so it requires the
+       // per-file key metadata produced by 
[StandardEncryptionManager.NewEncryptedOutputFile].
+       ErrKeyMetadataRequired = errors.New("encryption: 
StandardEncryptionManager requires non-empty key metadata")
+
+       // ErrUnsupportedKeyMetadataVersion is returned when key metadata was
+       // produced by a newer, incompatible encoding version.
+       ErrUnsupportedKeyMetadataVersion = errors.New("encryption: unsupported 
key metadata version")
+)
+
+// standardKeyMetadataVersion is the current encoding version written by
+// [StandardEncryptionManager]. It is bumped whenever the on-disk layout of
+// standardKeyMetadata or the block ciphertext format changes incompatibly.
+const standardKeyMetadataVersion = 1
+
+// standardKeyMetadata is the JSON-encoded structure stored as the opaque
+// [EncryptionKeyMetadata] for files produced by [StandardEncryptionManager].
+type standardKeyMetadata struct {
+       Version         int    `json:"v"`
+       KeyID           string `json:"key-id"`
+       WrappedKey      []byte `json:"wrapped-key"`
+       NoncePrefix     []byte `json:"nonce-prefix"`
+       BlockSize       int    `json:"block-size"`
+       PlaintextLength int64  `json:"plaintext-length"`
+}
+
+// StandardEncryptionManager is a generic, format-agnostic [EncryptionManager]
+// that provides envelope encryption for arbitrary files (e.g. manifests,
+// manifest lists, Puffin statistics) using a [KeyManagementClient] to wrap
+// and unwrap a fresh AES-256-GCM data encryption key (DEK) per file.
+//
+// Each file is split into fixed-size plaintext blocks, and each block is
+// sealed independently with AES-GCM using a unique nonce (a per-file random
+// prefix combined with the block index). This bounds memory usage and
+// supports random access (Seek/ReadAt) on the decrypted file without
+// buffering or decrypting more than the requested blocks.
+//
+// StandardEncryptionManager always encrypts and always decrypts: it fails
+// closed, returning [ErrKeyIDRequired] or [ErrKeyMetadataRequired] rather
+// than silently falling back to plaintext. Use [PlaintextEncryptionManager]
+// for tables or files that are not encrypted.
+type StandardEncryptionManager struct {

Review Comment:
   I think cross-engine interop isn't a goal for this PR as this is 
intentionally an iceberg-go-only envelope format for now (no header, 64 KB 
blocks, derived nonce, nil AAD, JSON key_metadata), not the AGS1 stream Java's 
StandardEncryptionManager produces. 
   
   If (or when) cross-engine interop becomes a requirement (which I agree it 
eventually needs to be, for tables written by one engine and read by another), 
we'd need to either adopt AGS1 directly or add a pluggable format layer. I'll 
bring this up on #1289.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to