Skip to content

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

Merged
atulpatildbz merged 4 commits intogoogle:mainfrom
atulpatildbz:wsd_decaps_go
Mar 3, 2026
Merged

[keymanager/wsd] Add /keys:decaps KOL API with DecapAndSeal + Open orchestration#650
atulpatildbz merged 4 commits intogoogle:mainfrom
atulpatildbz:wsd_decaps_go

Conversation

@atulpatildbz
Copy link
Copy Markdown
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

Testing

tested with encap operation manually.
Testing generate + decap + seal + open
Code for manual testing: atulpatildbz@ac4b947

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Comment thread keymanager/workload_service/server.go Outdated
return
}

encapsulatedKey, err := base64.StdEncoding.DecodeString(req.EncapsulatedKey)
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 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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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).

@atulpatildbz atulpatildbz force-pushed the wsd_decaps_go branch 5 times, most recently from acab338 to 4f935f5 Compare February 15, 2026 10:56
@atulpatildbz atulpatildbz force-pushed the wsd_decaps_go branch 7 times, most recently from 4deb925 to 448b861 Compare February 28, 2026 08:37
Comment thread keymanager/key_protection_service/service.go Outdated
Comment thread keymanager/workload_service/server.go Outdated
return
}

if req.Ciphertext.Algorithm != KemAlgorithmDHKEMX25519HKDFSHA256 {
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.

Look up from protobuf instead of hardcoding?

…ine HTTP handler methods, and improve KEM algorithm validation.
@jkl73 jkl73 requested a review from pgonda March 2, 2026 19:31
var outEncKey [32]byte
outEncKeyLen := C.size_t(len(outEncKey))
var outCT [48]byte // 32-byte secret + 16-byte GCM tag
outCTLen := C.size_t(len(outCT))
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.

Size magics here? Can you define as file consts?

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.

Added consts. Did the same in ws_key_custody_core_cgo.go‎ as well

Comment thread keymanager/key_protection_service/service.go Outdated
// Open decrypts a sealed ciphertext using the binding key identified by
// bindingUUID via Rust FFI (HPKE Open).
// Returns the decrypted plaintext (shared secret).
func Open(bindingUUID uuid.UUID, enc, ciphertext, aad []byte) ([]byte, error) {
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.

No need to change now, but this PR is pretty large by my eyes. Open an Decap operations could have been separated into to smaller more targeted PRs.

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.

Ack and apologies.
I didn't realize Decap + Open would grow to be so huge.

Thanks for taking the time to review

)
if rc != 0 {
return nil, fmt.Errorf("key_manager_open failed with code %d", rc)
}
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.

If RC isn't needed later. Typically in go we'd

if rc := ...; rc !=0 {
..
}

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.

Makes sense. done

Comment thread keymanager/workload_service/server.go Outdated
}
aad := decapsAADContext(kemUUID, req.Ciphertext.Algorithm)

// Step 1: Look up the binding UUID for this KEM 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.

Does this code base use this "Step #" comment format? If not please remove to be consistent

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.

Removed.

…ment

- Extracts magic numbers for byte array sizes into file-level constants
  for better readability.
- Refactors return code (rc) checks on C FFI calls to use idiomatic
  inline assignments.

docs: remove 'Step #' prefix from comments
Comment thread keymanager/key_protection_service/service.go
@NilanjanDaw NilanjanDaw self-requested a review March 3, 2026 09:12
@atulpatildbz atulpatildbz merged commit cbbc624 into google:main Mar 3, 2026
12 checks passed
mayafleischer pushed a commit to mayafleischer/go-tpm-tools that referenced this pull request Mar 4, 2026
…chestration (google#650)

* feat(keymanager): Implement Decap and Seal orchestration in Go

* refactor: consolidate KeyProtectionService interfaces, explicitly define HTTP handler methods, and improve KEM algorithm validation.

* review comments: refactor magic number and rc check, comments improvement

- Extracts magic numbers for byte array sizes into file-level constants
  for better readability.
- Refactors return code (rc) checks on C FFI calls to use idiomatic
  inline assignments.

docs: remove 'Step #' prefix from comments

* refactor: kps interface based DI for KCC
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.

3 participants