Skip to content

Comments

[keymanager/wsd] Add /keys:decaps KOL API with DecapAndSeal + Open orchestration#650

Open
atulpatildbz wants to merge 10 commits intogoogle:mainfrom
atulpatildbz:wsd_decaps_go
Open

[keymanager/wsd] Add /keys:decaps KOL API with DecapAndSeal + Open orchestration#650
atulpatildbz wants to merge 10 commits intogoogle:mainfrom
atulpatildbz:wsd_decaps_go

Conversation

@atulpatildbz
Copy link
Collaborator

@atulpatildbz atulpatildbz commented Feb 9, 2026

Implements the Go orchestration layer (KOL) for POST /v1/keys:decap. This key exchange endpoint allows workloads to recover a shared secret from a KEM encapsulation.

Flow:

  1. Workload sends {key_handle, ciphertext: {algorithm, ciphertext}} to WSD
  2. WSD looks up binding UUID from the KEM→Binding map (populated during /v1/keys:generate_kem)
  3. WSD calls KPS DecapAndSeal — decapsulates the shared secret using the KEM private key, reseals it with the binding public key
  4. WSD calls WSD KCC Open — unseals the shared secret using the binding private key
  5. WSD returns the plaintext shared secret (base64-encoded) to the workload

Changes:

  • Endpoint: Added POST /v1/keys:decap (aligned with API contract)
  • JSON: Used snake_case for JSON fields in DecapsRequest and DecapsResponse
  • C headers: Added key_manager_decap_and_seal (KPS) and key_manager_open (WSD) declarations
  • CGO bridges: Added DecapAndSeal() and Open() Go wrappers for the Rust FFI functions
  • KPS service: Extended Service with DecapAndSeal method
  • WSD server: Added DecapSealer/Opener interfaces, handleDecaps handler
  • Tests: Verified with TestHandleDecapsSuccess in and Rust unit tests

Dependencies

This PR is built on top of:

PR #652 ends at commit 2030fa6.

What to review

Please review commits from 4f935f5 onwards (after base commit 2030fa6).
The entire Decap implementation is squashed into this single commit:

  • 4f935f5 feat(keymanager): Implement Decap and Seal orchestration in Go

All commits prior to 4f935f5 are from PR #652 and PR #649 (already under separate review).

Copy link
Collaborator

@NilanjanDaw NilanjanDaw left a comment

Choose a reason for hiding this comment

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

Thanks for the PR. Over-all comment: I think we need to align the message formats for the APIs. Lets define them in a separate proto file, so that they can be easily reviewed.

Comment on lines +71 to +78
// Use a sentinel byte so the pointer is always valid.
var aadSentinel [1]byte
aadPtr := (*C.uint8_t)(unsafe.Pointer(&aadSentinel[0]))
aadLen := C.size_t(0)
if len(aad) > 0 {
aadPtr = (*C.uint8_t)(unsafe.Pointer(&aad[0]))
aadLen = C.size_t(len(aad))
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we add a AAD context for the HPKE seal/open operation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point. I added an explicit AAD context for the decaps flow and pass it through both KPS DecapAndSeal and WSD Open: "wsd:keys:decaps:v1::". Addressed in commit 02d8655.

return
}

encapsulatedKey, err := base64.StdEncoding.DecodeString(req.EncapsulatedKey)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we need to align the message format and the params a bit. The encapsulatedKey is expected to be raw bytes and packed as below

message DecapsRequest {
  KeyHandle key_handle = 1;
  KemCiphertext ciphertext = 2;
}

// The results of an Encaps operation.
message KemCiphertext {
  KemAlgorithm algorithm = 1;
  bytes ciphertext = 2;  // `Nenc` bytes long.
}

Copy link
Collaborator Author

@atulpatildbz atulpatildbz Feb 13, 2026

Choose a reason for hiding this comment

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

Updated to the requested shape: DecapsRequest{ keyHandle, ciphertext{ algorithm, ciphertext } }.
The proto contract is in keymanager/workload_service/proto/key_management.proto (commit 4d604da), and handler parsing/validation was updated in commit 02d8655.

Comment on lines 171 to 173
resp := DecapsResponse{
SharedSecret: base64.StdEncoding.EncodeToString(plaintext),
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Response should be encoded as follows

// The results of a Decaps operation.
message KemSharedSecret {
  KemAlgorithm algorithm = 1;
  bytes secret = 2;  // `Nsecret` bytes long.
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated response encoding to KemSharedSecret shape: sharedSecret{ algorithm, secret }. Implemented in commit 02d8655 (including test updates).

@atulpatildbz
Copy link
Collaborator Author

Thanks for the PR. Over-all comment: I think we need to align the message formats for the APIs. Lets define them in a separate proto file, so that they can be easily reviewed.

Thanks for the review. I aligned the API message formats and added a dedicated proto file: keymanager/workload_service/proto/key_management.proto (commit 4d604da). I then updated the WSD /keys:decaps handler/tests to use that proto-shaped payload/response (commit 02d8655).

NilanjanDaw and others added 9 commits February 15, 2026 09:43
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.
This change introduces automated C header generation using binding and fixes a critical ABI mismatch in the key custody FFI layer.

Changes:

1. FFI Header Generation:
   - Added keymanager/generate_ffi_headers.sh script to generate C headers for all key custody components.
   - Added cbindgen.toml configurations for km_common, kps_key_custody_core, and ws_key_custody_core.
   - Exposed km_common::ffi with stable C-ABI structs and constants to ensure compatibility.

2. FFI ABI Safety Fix:
   - Problem: Previous FFI functions accepted HpkeAlgorithm (Protobuf-generated repr(Rust) struct) by value. This caused undefined behavior as C callers expected a C-compatible layout.
   - Fix: Updated key_manager_generate_kem_keypair and key_manager_generate_binding_keypair to accept KmHpkeAlgorithm, a dedicated repr(C) struct.
   - Mechanism: Implemented safe conversion (impl From<KmHpkeAlgorithm> for HpkeAlgorithm) to bridge the FFI boundary safely.
   - Verification: Added FFI-specific tests covering KmHpkeAlgorithm usage.
    - Implement binding and KEM key generation endpoints in Workload Service.
    - Add Key Protection Service client integration.
    - Adapt Go CGO wrappers to match new Rust FFI signatures (KmHpkeAlgorithm, usize).
    - Add component integration tests (Go Service -> Rust Core) to verify FFI flow.
Updates the EnumerateKEMKeys API to use snake_case JSON tags and string
serialization for enums (KemAlgorithm, KeyProtectionMechanism) to align
with the defined API contract.
- Update GenerateKemRequest and GenerateKemResponse JSON tags to use
snake_case.

- Update KemAlgorithm enum string representation to remove
KEM_ALGORITHM_ prefix.

- Update tests to reflect these changes.
@atulpatildbz atulpatildbz force-pushed the wsd_decaps_go branch 4 times, most recently from bc9f760 to acab338 Compare February 15, 2026 10:54
This change implements the Decap and Seal flow in the Workload Service.
It includes:
- POST /v1/keys:decap endpoint
- DecapAndSeal (KPS) and Open (WSD) orchestration
- AAD binding for decapsulation
- DecapsRequest and DecapsResponse proto definitions
- FFI integration for Decap and Seal
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.

2 participants