adamreeve commented on code in PR #49667:
URL: https://github.com/apache/arrow/pull/49667#discussion_r3097782784


##########
python/pyarrow/_parquet_encryption.pyx:
##########
@@ -711,3 +711,186 @@ cdef shared_ptr[CDecryptionConfiguration] 
pyarrow_unwrap_decryptionconfig(object
     if isinstance(decryptionconfig, DecryptionConfiguration):
         return (<DecryptionConfiguration> decryptionconfig).unwrap()
     raise TypeError("Expected DecryptionConfiguration, got %s" % 
type(decryptionconfig))
+
+
+def create_decryption_properties(
+    footer_key,
+    *,
+    aad_prefix=None,
+    bint check_footer_integrity=True,
+    bint allow_plaintext_files=False,
+):
+    """
+    Create FileDecryptionProperties using a direct footer key.
+
+    This bypasses the KMS-based :class:`CryptoFactory` API and directly
+    constructs decryption properties from a plaintext key. This is useful
+    when the caller manages key wrapping externally (e.g. via an
+    application-level envelope encryption scheme).
+
+    For most use cases, prefer the higher-level :class:`CryptoFactory`
+    with :class:`DecryptionConfiguration`, which handles envelope
+    encryption and key rotation automatically.
+
+    Parameters
+    ----------
+    footer_key : bytes
+        The decryption key for the file footer (and all columns if
+        uniform encryption was used). Must be 16, 24, or 32 bytes
+        for AES-128, AES-192, or AES-256 respectively.
+    aad_prefix : bytes, optional
+        Additional Authenticated Data prefix. Must match the AAD prefix
+        that was used during encryption. Required if the file was written
+        with ``store_aad_prefix=False``.
+    check_footer_integrity : bool, default True
+        Whether to verify footer integrity using the signature stored
+        in the file. Set to False only for debugging.
+    allow_plaintext_files : bool, default False
+        Whether to allow reading plaintext (unencrypted) files with
+        these decryption properties without raising an error.
+
+    Returns
+    -------
+    FileDecryptionProperties
+        Properties that can be passed to :func:`read_table`,
+        :class:`ParquetFile`, or
+        :class:`~pyarrow.dataset.ParquetFragmentScanOptions`.
+
+    Examples
+    --------
+    >>> import pyarrow.parquet as pq
+    >>> import pyarrow.parquet.encryption as pe
+    >>> props = pe.create_decryption_properties(
+    ...     footer_key=b'0123456789abcdef',
+    ...     aad_prefix=b'table_id'
+    ... )
+    >>> table = pq.read_table('encrypted.parquet', decryption_properties=props)
+    """
+    cdef:
+        CSecureString c_footer_key
+        c_string c_aad_prefix
+        CFileDecryptionPropertiesBuilder* builder
+        shared_ptr[CFileDecryptionProperties] props
+
+    footer_key_bytes = tobytes(footer_key)
+    if len(footer_key_bytes) not in (16, 24, 32):
+        raise ValueError(
+            f"footer_key must be 16, 24, or 32 bytes, got 
{len(footer_key_bytes)}"
+        )
+
+    c_footer_key = CSecureString(<c_string>footer_key_bytes)
+    builder = new CFileDecryptionPropertiesBuilder()
+
+    try:
+        builder.footer_key(c_footer_key)
+
+        if aad_prefix is not None:
+            c_aad_prefix = tobytes(aad_prefix)
+            builder.aad_prefix(c_aad_prefix)
+
+        if not check_footer_integrity:
+            builder.disable_footer_signature_verification()
+
+        if allow_plaintext_files:
+            builder.plaintext_files_allowed()
+
+        props = builder.build()
+    finally:
+        del builder
+
+    return FileDecryptionProperties.wrap(props)
+
+
+def create_encryption_properties(
+    footer_key,
+    *,
+    aad_prefix=None,
+    bint store_aad_prefix=True,
+    encryption_algorithm="AES_GCM_V1",
+    bint plaintext_footer=False,
+):
+    """
+    Create FileEncryptionProperties using a direct footer key.
+
+    This bypasses the KMS-based :class:`CryptoFactory` API and directly
+    constructs encryption properties from a plaintext key. This is useful
+    when the caller manages key wrapping externally (e.g. via an
+    application-level envelope encryption scheme).
+
+    For most use cases, prefer the higher-level :class:`CryptoFactory`
+    with :class:`EncryptionConfiguration`, which handles envelope
+    encryption, key rotation, and unique-per-file data keys
+    automatically.
+
+    Parameters
+    ----------
+    footer_key : bytes
+        The encryption key for the file footer (and all columns unless
+        per-column keys are specified). Must be 16, 24, or 32 bytes
+        for AES-128, AES-192, or AES-256 respectively.

Review Comment:
   Similar to above, we should add a note somewhere about per-column keys not 
being supported yet.



##########
python/pyarrow/_parquet_encryption.pyx:
##########
@@ -711,3 +711,186 @@ cdef shared_ptr[CDecryptionConfiguration] 
pyarrow_unwrap_decryptionconfig(object
     if isinstance(decryptionconfig, DecryptionConfiguration):
         return (<DecryptionConfiguration> decryptionconfig).unwrap()
     raise TypeError("Expected DecryptionConfiguration, got %s" % 
type(decryptionconfig))
+
+
+def create_decryption_properties(
+    footer_key,
+    *,
+    aad_prefix=None,
+    bint check_footer_integrity=True,
+    bint allow_plaintext_files=False,
+):
+    """
+    Create FileDecryptionProperties using a direct footer key.
+
+    This bypasses the KMS-based :class:`CryptoFactory` API and directly
+    constructs decryption properties from a plaintext key. This is useful
+    when the caller manages key wrapping externally (e.g. via an
+    application-level envelope encryption scheme).
+
+    For most use cases, prefer the higher-level :class:`CryptoFactory`
+    with :class:`DecryptionConfiguration`, which handles envelope
+    encryption and key rotation automatically.
+
+    Parameters
+    ----------
+    footer_key : bytes
+        The decryption key for the file footer (and all columns if
+        uniform encryption was used). Must be 16, 24, or 32 bytes

Review Comment:
   It looks like this only supports uniform encryption at the moment, so this 
comment could be a little confusing. Maybe there should be a note about this 
added here, eg. "Note that currently only uniform encryption is supported with 
this method"



##########
python/pyarrow/_parquet_encryption.pyx:
##########
@@ -711,3 +711,186 @@ cdef shared_ptr[CDecryptionConfiguration] 
pyarrow_unwrap_decryptionconfig(object
     if isinstance(decryptionconfig, DecryptionConfiguration):
         return (<DecryptionConfiguration> decryptionconfig).unwrap()
     raise TypeError("Expected DecryptionConfiguration, got %s" % 
type(decryptionconfig))
+
+
+def create_decryption_properties(
+    footer_key,
+    *,
+    aad_prefix=None,
+    bint check_footer_integrity=True,
+    bint allow_plaintext_files=False,
+):
+    """
+    Create FileDecryptionProperties using a direct footer key.
+
+    This bypasses the KMS-based :class:`CryptoFactory` API and directly
+    constructs decryption properties from a plaintext key. This is useful
+    when the caller manages key wrapping externally (e.g. via an
+    application-level envelope encryption scheme).
+
+    For most use cases, prefer the higher-level :class:`CryptoFactory`
+    with :class:`DecryptionConfiguration`, which handles envelope
+    encryption and key rotation automatically.

Review Comment:
   I don't think it's true that the CryptoFactory handles key rotation 
automatically, that part of the comment should probably be removed. It does 
allow key rotation if you use external key material though.



##########
python/pyarrow/_parquet_encryption.pyx:
##########
@@ -711,3 +711,186 @@ cdef shared_ptr[CDecryptionConfiguration] 
pyarrow_unwrap_decryptionconfig(object
     if isinstance(decryptionconfig, DecryptionConfiguration):
         return (<DecryptionConfiguration> decryptionconfig).unwrap()
     raise TypeError("Expected DecryptionConfiguration, got %s" % 
type(decryptionconfig))
+
+
+def create_decryption_properties(
+    footer_key,
+    *,
+    aad_prefix=None,
+    bint check_footer_integrity=True,
+    bint allow_plaintext_files=False,
+):
+    """
+    Create FileDecryptionProperties using a direct footer key.
+
+    This bypasses the KMS-based :class:`CryptoFactory` API and directly
+    constructs decryption properties from a plaintext key. This is useful
+    when the caller manages key wrapping externally (e.g. via an
+    application-level envelope encryption scheme).
+
+    For most use cases, prefer the higher-level :class:`CryptoFactory`
+    with :class:`DecryptionConfiguration`, which handles envelope
+    encryption and key rotation automatically.
+
+    Parameters
+    ----------
+    footer_key : bytes
+        The decryption key for the file footer (and all columns if
+        uniform encryption was used). Must be 16, 24, or 32 bytes
+        for AES-128, AES-192, or AES-256 respectively.
+    aad_prefix : bytes, optional
+        Additional Authenticated Data prefix. Must match the AAD prefix
+        that was used during encryption. Required if the file was written
+        with ``store_aad_prefix=False``.
+    check_footer_integrity : bool, default True
+        Whether to verify footer integrity using the signature stored
+        in the file. Set to False only for debugging.
+    allow_plaintext_files : bool, default False
+        Whether to allow reading plaintext (unencrypted) files with
+        these decryption properties without raising an error.
+
+    Returns
+    -------
+    FileDecryptionProperties
+        Properties that can be passed to :func:`read_table`,
+        :class:`ParquetFile`, or
+        :class:`~pyarrow.dataset.ParquetFragmentScanOptions`.
+
+    Examples
+    --------
+    >>> import pyarrow.parquet as pq
+    >>> import pyarrow.parquet.encryption as pe
+    >>> props = pe.create_decryption_properties(
+    ...     footer_key=b'0123456789abcdef',
+    ...     aad_prefix=b'table_id'
+    ... )
+    >>> table = pq.read_table('encrypted.parquet', decryption_properties=props)
+    """
+    cdef:
+        CSecureString c_footer_key
+        c_string c_aad_prefix
+        CFileDecryptionPropertiesBuilder* builder
+        shared_ptr[CFileDecryptionProperties] props
+
+    footer_key_bytes = tobytes(footer_key)
+    if len(footer_key_bytes) not in (16, 24, 32):
+        raise ValueError(
+            f"footer_key must be 16, 24, or 32 bytes, got 
{len(footer_key_bytes)}"
+        )
+
+    c_footer_key = CSecureString(<c_string>footer_key_bytes)
+    builder = new CFileDecryptionPropertiesBuilder()
+
+    try:
+        builder.footer_key(c_footer_key)
+
+        if aad_prefix is not None:
+            c_aad_prefix = tobytes(aad_prefix)
+            builder.aad_prefix(c_aad_prefix)
+
+        if not check_footer_integrity:
+            builder.disable_footer_signature_verification()
+
+        if allow_plaintext_files:
+            builder.plaintext_files_allowed()
+
+        props = builder.build()
+    finally:
+        del builder
+
+    return FileDecryptionProperties.wrap(props)
+
+
+def create_encryption_properties(
+    footer_key,
+    *,
+    aad_prefix=None,
+    bint store_aad_prefix=True,
+    encryption_algorithm="AES_GCM_V1",
+    bint plaintext_footer=False,
+):
+    """
+    Create FileEncryptionProperties using a direct footer key.
+
+    This bypasses the KMS-based :class:`CryptoFactory` API and directly
+    constructs encryption properties from a plaintext key. This is useful
+    when the caller manages key wrapping externally (e.g. via an
+    application-level envelope encryption scheme).
+
+    For most use cases, prefer the higher-level :class:`CryptoFactory`
+    with :class:`EncryptionConfiguration`, which handles envelope
+    encryption, key rotation, and unique-per-file data keys

Review Comment:
   ```suggestion
       encryption and unique-per-file data keys
   ```



-- 
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]

Reply via email to