Skip to content

Wiring DBPAgent into ExternalEncryptorImpl/ExternalDecryptor impl - #68

Merged
argmarco-tkd merged 1 commit into
dev-miniappfrom
dll_work_eei_impl
Aug 20, 2025
Merged

Wiring DBPAgent into ExternalEncryptorImpl/ExternalDecryptor impl#68
argmarco-tkd merged 1 commit into
dev-miniappfrom
dll_work_eei_impl

Conversation

@argmarco-tkd

@argmarco-tkd argmarco-tkd commented Aug 20, 2025

Copy link
Copy Markdown
Collaborator

Rationale for this change

In this work, we're wiring the shared library (aka DLL) -loaded DBPAgent instance into both ExternalEncryptorImpl and ExternalDecryptorImpl (EEI and EDI, respectively). We're using the miniApp branch for this.

As part of this change, the previous implementations of EEI and EDI are being over-written.

As a reminder - this particular PR will never be merged into dev_phase2 nor into main - an equivalent change will be made once the refactoring work happening in dev_phase2 is complete.

What changes are included in this PR?

  • Addition of dbpa_utils.h and dbpa_utils.cc as utils to help translate concepts between the Arrow and DBPA namespace.
  • Heavily modified ExternalEncryptorImpl and ExternalDecryptorImpl (inside encryption_internal.cc and encryption_internal.h).
    • In particular, we modified
      • Constructors
      • Implementation of the ::Make method
      • Implementations of ::Encrypt() ::Decrypt() CiphertextLength() and PlaintextLength()
      • Eliminated the underlying usage of AESEncryptor/AESDecryptor
    • We did not modify
      • The EncryptorInterface and DecryptorInterface definitions (again, this is the mini-App. We left them as-is).

Are these changes tested?

  • Mostly.
    • The encryption workflow works (verified using the miniApp).
    • Existing unit tests pass
  • The Decryption workflow is untested, but that's because a larger change in the Python->Cython->C++ change is required (coming in a later PR).

Related

This implements

But in both cases leaves the Executor outside of the implementation. Executor for both will be picked up in a later task:
protegrity/DataBatchProtectionService#73

@github-actions

Copy link
Copy Markdown

Thanks for opening a pull request!

If this is not a minor PR. Could you open an issue for this pull request on GitHub? https://github.com/apache/arrow/issues/new/choose

Opening GitHub issues ahead of time contributes to the Openness of the Apache Arrow project.

Then could you also rename the pull request title in the following format?

GH-${GITHUB_ISSUE_ID}: [${COMPONENT}] ${SUMMARY}

or

MINOR: [${COMPONENT}] ${SUMMARY}

See also:

@argmarco-tkd
argmarco-tkd force-pushed the dll_work_eei_impl branch 2 times, most recently from 9b99922 to 5786d51 Compare August 20, 2025 06:42
@argmarco-tkd argmarco-tkd changed the title Dll work eei impl Wiring DBPAgent into ExternalEncryptorImpl/ExternalDecryptor impl Aug 20, 2025
@avalerio-tkd

Copy link
Copy Markdown
Collaborator

@argmarco-tkd I think you meant to put us as Reviewers, not Assignees :)
I updated it and will review :)

@avalerio-tkd avalerio-tkd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gave it a first pass. Added a few comments. Thanks!

std::cout << " compression_type: " << CompressName(compression_type) << std::endl;
std::cout << " encoding: " << encoding << std::endl;
std::cout << " ext_column_key: " << ext_column_key << std::endl;
std::cout << " user_id: " << user_id << std::endl;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have the user_id at this level, since we moved it inside app_context?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, but this being miniapp code, I think it's around there.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is based on the mini-app, where the user_id is/was a first class citizen - so we've kept it. In the final version (whatever we eventually merge into dev_phase_2 and/or protegrity/main), we won't have user_id as a first class citizen - and it it will be part of app_context.

@avalerio-tkd avalerio-tkd Aug 20, 2025

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! Yes, of course. Forgot for a minute. All good!

namespace parquet::encryption {

// Forward declarations for utility functions
std::string HackTypeToString(Type::type t);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a TODO note if this is temporary

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the changes to this file are temporary :). Like I mentioned in the PR description, most of this code (ExternalEncryptorImpl, ExternalDecryptorImpl) will be re-written (as opposed to merged) once we unite the two workstreams we have going on.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect, that's true that we'll rewrite EEI and EDI when merged. Thanks for that.


if (!result->success()) {
std::cout << "[ERROR] Encryption failed: " << result->error_message() << std::endl;
throw ParquetException(result->error_message());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where will this exception land, do we know? Will this be part of the error handling afterwards? Let's add a TODO note for that if it's the case.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Today this is propagated all the way up to the App. This will be covered as part of error handling. Will add a TODO.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I though. All good. Thanks for the TODO.
Will add a comment on the Issue for error handling to check on the "throw" statements.

std::cout << " encrypted_footer buffer size: " << encrypted_footer.size() << " bytes" << std::endl;

std::cout << "[DEBUG] Calling agent_instance_->Encrypt for footer..." << std::endl;
std::unique_ptr<EncryptionResult> result = agent_instance_->Encrypt(footer);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory we won't use this encryptor for footers or anything else. Not sure what the datatype would be if any? In that case, should we just throw a NotImplementedExc or something like that?

@sofia-tekdatum thoughts? Do you know if this method is required if we only encrypt column chunks, not metadata?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is part of the signature verification done when a parquet file is going to be decrypted.

So we are going to need to implement it, I'll have more details when I finish one of the tasks in this sprint related to that.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is remnant of the mini-app code. From what I understand, this (SignedFooterEncrypt()) won't be part of the final solution. The method is used for footer verification inside parquet/metadata.cc, via direct instantiation of AesEncryptor). Created a task to discuss, as this affects the definition of EncryptorInterface but is not exactly relevant to this PR.

https://github.com/protegrity/DataBatchProtectionService/issues/74

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect. Thanks for the tracking task!

//TODO
// This is not production code. We know that the one DPBA Agent we have uses XOR encryption.
// Therefore it's safe to assume that the ciphertext length is the same as the plaintext length.
// This is not true for all DPBA Agents.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

std::cout << "Created ExternalDecryptorImpl" << std::endl;
ExternalDecryptorImpl::ExternalDecryptorImpl(
std::unique_ptr<DataBatchProtectionAgentInterface> agent_instance)
: agent_instance_(std::move(agent_instance)) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the ExternalEncrypt/Decrypt will share an instance of the DBPA ? Can we add a comment on that if it's the case?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They do not and will not share an instance (at least that's the current plan). Each (EEI, EDI) will make a call to load an independent instance from the DLL.

This does not stop us from having each agent instance share state (via class variables or similar).

Added an FAQ entry (based on earlier discussion on the topic) here

happy to discuss.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the FAQ. We can chat offline. Not a blocker for this PR.


static std::unique_ptr<ExternalDecryptorImpl> Make(ParquetCipher::type alg_id, int32_t key_len,
bool metadata);
static std::unique_ptr<ExternalDecryptorImpl> Make(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a mental note that we needed to talk about the params of Make, but now I think is ok, right? Let us know if we need to talk further. If not, all good.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're good (i.e. no discussion needed :) ). The parameters here will change once we have the final implementation - a lot of the current params are just vestigial from the miniapp code.

static std::unique_ptr<ExternalDecryptorImpl> Make(ParquetCipher::type alg_id, int32_t key_len,
bool metadata);
static std::unique_ptr<ExternalDecryptorImpl> Make(
ParquetCipher::type alg_id, int32_t key_len, std::string column_name, Type::type data_type,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so if I understand the pattern, you're intending for the Arrow code to call this Make, and then within the Make we call the constructor of the DBPAgentInterface, right?

Just checking because I'd rather not leak any of those classes out where the encryptors are created.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The objective here is for any ExternalEncryptorImpls (final name TBD) to be instantiated the same - regardless of what they do underneath. This should (hopefully) be the same instantiation mechanism as for other encryptors/decryptors. Once the instantiation mechanism is invoked, EEI will internally do its thing.

For this PR (given that the changed to EEI and EDI are mostly throaway) I wanted to minimize the amount of changes and piggy-backed as much as possible onto the current ::Make builder method.

Whether the instantiation happens via ::Make() or via a constructor is somewhat irrelevant (but I believe Arrow currently prefers the utilization of the ::Make() builder method).

bool metadata);
static std::unique_ptr<ExternalDecryptorImpl> Make(
ParquetCipher::type alg_id, int32_t key_len, std::string column_name, Type::type data_type,
Compression::type compression_type, Encoding::type encoding, std::string ext_column_key,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following the discussion we had over this doc:
https://docs.google.com/document/d/1goKWGR19UsVyXXQrxUPUI-1GBSxN6Yc3qEp2gykVYrg/edit?tab=t.0#heading=h.rdqcrfsd11cy

Encoding is not something you're expecting from Arrow?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different type of encoding :)

In that document, the 'encoding' (under the "Format and Encoding" section) refers to a network-transport concept, which is only pertinent to the DBPSClient <-> DBPServer communication.

The Encoding here (in the ::Make) defines how data is physically encoded in the Parquet files.

@avalerio-tkd - please verify.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Marco is right, but also not sure how this Encoding::type encoding propagates on Make. Let me check offline with Sofi and will confirm.

std::cout << " compression_type: " << CompressName(compression_type) << std::endl;
std::cout << " encoding: " << encoding << std::endl;
std::cout << " ext_column_key: " << ext_column_key << std::endl;
std::cout << " user_id: " << user_id << std::endl;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, but this being miniapp code, I think it's around there.

std::cout << " encrypted_footer buffer size: " << encrypted_footer.size() << " bytes" << std::endl;

std::cout << "[DEBUG] Calling agent_instance_->Encrypt for footer..." << std::endl;
std::unique_ptr<EncryptionResult> result = agent_instance_->Encrypt(footer);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is part of the signature verification done when a parquet file is going to be decrypted.

So we are going to need to implement it, I'll have more details when I finish one of the tasks in this sprint related to that.

@avalerio-tkd

Copy link
Copy Markdown
Collaborator

@argmarco-tkd let me confirm with Sofi about the Encoding param in Make. Otherwise it LGTM.

@avalerio-tkd avalerio-tkd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM+. Will sync with Sofi about the Encoding and we can revisit if needed.

@argmarco-tkd
argmarco-tkd merged commit e3a1a02 into dev-miniapp Aug 20, 2025
37 of 104 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants