[KeyManager] Implement FFIs for decap-and-seal and HPKE open operations#649
[KeyManager] Implement FFIs for decap-and-seal and HPKE open operations#649NilanjanDaw wants to merge 11 commits intogoogle:mainfrom
Conversation
49aeabd to
20bc382
Compare
This change functions as the Go-side implementation for the Decap and Seal flow. It is based on PR google#652 (Key Gen) and PR google#649 (FFI Decap). Base Commit: 2030fa6 (Align Key Generation API with contract) Squashed Commits: - Fix compilation and tests for wsd_decaps_go (API alignment to /v1/keys:decap) - keymanager/wsd: align decaps payloads with proto messages - keymanager/wsd: define decaps API proto schema - [keymanager/wsd] Add /keys:decaps endpoint with DecapAndSeal + Open orchestration Key Features: - endpoint: POST /v1/keys:decap - Request: DecapsRequest (snake_case) - Response: DecapsResponse (snake_case) - Flows: DecapAndSeal (KPS) -> Open (WSD)
20bc382 to
cb4a8ec
Compare
cb4a8ec to
a7336b2
Compare
Introduces `key_manager_decap_and_seal`, a new FFI function in `key_custody_core` that enables decapsulating a shared secret and immediately resealing it with a binding public key. Key Changes: - New FFI: Added `key_manager_decap_and_seal` to handle the transition of a shared secret from a client-provided encapsulation to a sealed form bound to a specific receiver. - Security: Integrated `zeroize` to ensure the intermediate shared secret is wiped from memory after the resealing operation. - Error Handling: Implemented robust error reporting for invalid inputs, missing keys, and cryptographic failures. - Testing: Added a comprehensive unit test `test_decap_and_seal_success` that simulates a client-to-service flow and verifies the recovered secret.
BoringSSL does not currently expose a way to initialize a RecipientContext directly from a pre-calculated shared secret. This change adds a helper to km_common that manually implements the HPKE KeySchedule to derive the AEAD key and nonce. Changes: - Implement and HPKE KDF helpers in km_common. - Mark these helpers as available only for tests or via the new feature. - Enable in kps_key_custody_core dev-dependencies. - Update to verify decryption using the recovered secret.
This change adds the FFI function to the workload service's key custody core. This function allows the Workload Service to decrypt secrets using a previously generated binding key. Key features: - Look up the Binding Private Key from the registry (isolated in its own memfd_secret page via Vault). - Perform an HPKE open operation to decrypt the provided payload. - Aggressively sanitize sensitive material by zeroing out decrypted plaintext buffers after copying to the output. - Add comprehensive unit tests covering success, invalid UUID, and insufficient buffer size scenarios. - Fix unused import warnings in km_common related to test-only crypto helpers.
| Err(e) => e, | ||
| } | ||
| })) | ||
| .unwrap_or(-1) |
There was a problem hiding this comment.
The FFI API has a pretty opaque error code interface. Any future plans for improving this?
Once pattern I've seen before is setting a global ERRNO with an error code for the last failure, and always returning -1 in the FFI API.
There was a problem hiding this comment.
Let me do this in a future PR. We will track this in #668
|
|
||
| [dev-dependencies] | ||
| km_common = { path = "../../km_common", features = ["test-utils"] } |
There was a problem hiding this comment.
It might be useful to use a "Cargo workspace" to manage all of these shared crates:
https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html
There was a problem hiding this comment.
Done, updated the cargo dependencies to a workspace.
b653fa6 to
036d7f6
Compare
| use km_common::key_types::{KeyRecord, KeyRegistry, KeySpec}; | ||
| use prost::Message; | ||
| use std::slice; | ||
| use std::sync::atomic::AtomicBool; |
There was a problem hiding this comment.
nit: can you format this file? i think the previous position of this import was correct
| uuid: Uuid, | ||
| encapsulated_key: &[u8], | ||
| aad: &[u8], | ||
| ) -> Result<(Vec<u8>, Vec<u8>), i32> { |
There was a problem hiding this comment.
should we avoid the intermediate allocation and accept &mut [u8] instead? since caller already provides out_encapsulated_key, out_ciphertext output buffers
| Some(_) => 0, // Success | ||
| None => -1, // Not found | ||
| // Seal | ||
| match km_common::crypto::hpke_seal(binding_public_key, &shared_secret, aad, hpke_algo) { |
There was a problem hiding this comment.
we're using hpke_algo here. which assumes that binding key always supports the exact same cryptro params (KEM/KDF/AEAD) as the KEM key.
is your comment of the other PR #665 (comment) valid here?
| None => -1, // Not found | ||
| /// Internal function to decrypt a ciphertext using a stored binding key. | ||
| fn open_internal(uuid: Uuid, enc: &[u8], ciphertext: &[u8], aad: &[u8]) -> Result<SecretBox, i32> { | ||
| let Some(record) = KEY_REGISTRY.get_key(&uuid) else { |
There was a problem hiding this comment.
nit: we can use shorter idiom here:
let key_record = KEY_REGISTRY.get_key(&uuid).ok_or(-1)?;
| /// * `out_plaintext_len` - The size of `out_plaintext` buffer. | ||
| /// On success, it will be updated with the actual size of the plaintext. |
There was a problem hiding this comment.
the signature is :
out_plaintext_len: usize, // Passed by value!
this statement is misleading: "On success, it will be updated with the actual size of the plaintext". since it's passed by value.
check if you need to update the signature to *mut usize. or fix the docstring
This PR implements the core FFI interfaces for the Key Protection Service (KPS) and Workload Service (WS) to support the secure secret delivery flow using the
decap-and-sealand HPKEopenoperations. It also introduces necessary cryptographic utilities to test the end-to-end secret sharing flow.Key Changes
km_common (Common Crypto & Utilities)
encapto support encapsulation operation flows with a pre-shared key. This manually implements the Encap operation as BoringSSL's current FFI lacks a direct entry point encapsulation.kps_key_custody_core (Key Protection Service)
key_manager_decap_and_sealFFI: Implemented the decapsulate-and-seal logic. This takes a client's encapsulated key, recovers the shared secret using the KEM private key, and immediately reseals it for the target Workload Service using its binding public key.ws_key_custody_core (Workload Service)
key_manager_openFFI: Implemented the final decryption step. It retrieves the Binding Private Key (isolated in memfd_secret backed storage) to decrypt the final payload delivered from the KPS.Testing Performed