Skip to content

feat(encryption) [3/N] Support encryption: KMS#2339

Open
xanderbailey wants to merge 8 commits intoapache:mainfrom
xanderbailey:xb/kms_em
Open

feat(encryption) [3/N] Support encryption: KMS#2339
xanderbailey wants to merge 8 commits intoapache:mainfrom
xanderbailey:xb/kms_em

Conversation

@xanderbailey
Copy link
Copy Markdown
Contributor

@xanderbailey xanderbailey commented Apr 16, 2026

Which issue does this PR close?

Part of #2034

What changes are included in this PR?

Adds the KeyManagementClient trait and an in-memory implementation for testing.

  • KeyManagementClient trait mirrors Java's KeyManagementClient interface.
  • InMemoryKeyManagementClient for testing-only KMS that stores master keys in memory to wrap/unwrap. Supports configurable key sizes and explicit key bytes for cross-language interop tests.

Are these changes tested?

Tests covered for InMemoryKeyManagementClient

/// If `true`, callers can use [`generate_key`](Self::generate_key) for atomic
/// key generation and wrapping, which is more secure than generating a key
/// locally and then wrapping it.
fn supports_key_generation(&self) -> bool {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

A note on the supports_key_generation() + generate_key() pattern: this mirrors Java's interface. The supports_key_generation bool is a runtime capability check. Not sure if this is very idiomatic, I kept the Java pattern here since the EncryptionManager (coming in a later PR) needs to branch on this to decide between kms.generate_key() (atomic server-side generation, e.g. AWS KMS GenerateDataKey) vs generating locally + kms.wrap_key(). Open to feedback on whether we should do something else here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it's fine to use this pattern. But I think we don't need to provide default implementation? Java provides default implementation to avoid breaking compatibility, we don't need this.

Copy link
Copy Markdown
Member

@Xuanwo Xuanwo left a comment

Choose a reason for hiding this comment

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

Mostly LGTM, only a naming question.

///
/// Returned by [`KeyManagementClient::generate_key`] when the KMS supports
/// atomic key generation and wrapping.
pub struct KeyGenerationResult {
Copy link
Copy Markdown
Member

@Xuanwo Xuanwo Apr 17, 2026

Choose a reason for hiding this comment

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

Result is so widely used in rust. It can be a bit weired to read Result<KeyGenerationResult>

How about finding a better name?

(I'm not strong on this)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's a very fair point, have changed to GeneratedKey? I think that's better maybe?

@xanderbailey xanderbailey requested a review from Xuanwo April 17, 2026 01:18
Copy link
Copy Markdown
Collaborator

@mbutrovich mbutrovich left a comment

Choose a reason for hiding this comment

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

Thanks @xanderbailey, the trait design is clean and the InMemoryKeyManagementClient is a solid test utility.

One minor nit: the key_size field on InMemoryKeyManagementClient controls the size of randomly generated master keys, but could be misread as controlling wrap/unwrap behavior. Since this is a testing utility that people will read as example code, something like master_key_size might be clearer about what it governs.

@xanderbailey
Copy link
Copy Markdown
Contributor Author

Thanks @xanderbailey, the trait design is clean and the InMemoryKeyManagementClient is a solid test utility.

One minor nit: the key_size field on InMemoryKeyManagementClient controls the size of randomly generated master keys, but could be misread as controlling wrap/unwrap behavior. Since this is a testing utility that people will read as example code, something like master_key_size might be clearer about what it governs.

60989d2

Copy link
Copy Markdown
Contributor

@blackmwk blackmwk left a comment

Choose a reason for hiding this comment

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

Thanks @xanderbailey for this pr! Generally LGTM, just left some commets to resolve.

#[async_trait]
pub trait KeyManagementClient: Send + Sync + std::fmt::Debug {
/// Wrap (encrypt) a key using a wrapping key managed by the KMS.
async fn wrap_key(&self, key: &[u8], wrapping_key_id: &str) -> Result<Vec<u8>>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should this also be SensitiveBytes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The wrapped key is cipher text so I think this is fine.

/// If `true`, callers can use [`generate_key`](Self::generate_key) for atomic
/// key generation and wrapping, which is more secure than generating a key
/// locally and then wrapping it.
fn supports_key_generation(&self) -> bool {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it's fine to use this pattern. But I think we don't need to provide default implementation? Java provides default implementation to avoid breaking compatibility, we don't need this.

///
/// Returned by [`KeyManagementClient::generate_key`] when the KMS supports
/// atomic key generation and wrapping.
pub struct GeneratedKey {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should avoid pub field pattern.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rename it to memory.rs

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

/// # }
/// ```
#[derive(Clone)]
pub struct InMemoryKeyManagementClient {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
pub struct InMemoryKeyManagementClient {
pub struct MemoryKeyManagementClient {

Following conventions in other structs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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


impl InMemoryKeyManagementClient {
/// Creates a new in-memory KMS with 128-bit AES keys.
pub fn new() -> Self {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We could derive default if we make AesKeySize::Bits128 default?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@xanderbailey xanderbailey requested a review from blackmwk April 22, 2026 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants