Skip to content

[KCC] Add generate_kem_keypair and generate_binding_keypair FFIs#633

Open
meetrajvala wants to merge 1 commit intogoogle:mainfrom
meetrajvala:kcc1
Open

[KCC] Add generate_kem_keypair and generate_binding_keypair FFIs#633
meetrajvala wants to merge 1 commit intogoogle:mainfrom
meetrajvala:kcc1

Conversation

@meetrajvala
Copy link
Contributor

No description provided.

Copy link
Collaborator

@atulpatildbz atulpatildbz left a comment

Choose a reason for hiding this comment

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

On-going review. Will continue tomorrow. Meanwhile you can start working on these comments.

Comment on lines +13 to +16
#[derive(ZeroizeOnDrop)]
pub struct Vault {
mmap: MmapMut,
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

MmapMut doesn't implement Zeroize it seems. https://docs.rs/memmap2/latest/memmap2/struct.MmapMut.html#trait-implementations

Implement Drop manually to zero the mmap contents via self.mmap[..].zeroize() before unmapping.

let name_cstr = CString::new(name)
.map_err(|_| IoError::new(std::io::ErrorKind::InvalidInput, "Invalid name"))?;
let fd = unsafe {
libc::syscall(libc::SYS_memfd_create, name_cstr.as_ptr(), MFD_CLOEXEC | MFD_SECRET)
Copy link
Collaborator

Choose a reason for hiding this comment

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

MFD_SECRET isn't a valid flag for memfd_create. https://man7.org/linux/man-pages/man2/memfd_create.2.html

  1. Call the actual memfd_secret syscall . (man page reference)
  2. Use ftruncate to set the size.
  3. Use mmap to write the data (since memfd_secret fds don't support write()).

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah I think I wrote MFD_SECRET in my doc. It should be MFD_NOEXEC_SEAL instead. We should add it to prevent this memory from ever being executable.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Regarding using ftruncate, while it is a valid method. A memory write() should have similar effects. As the man-page mentions:

The initial size of the file is set to 0. Following the call, the
file size should be set using ftruncate(2). (Alternatively, the
file may be populated by calls to write(2) or similar.)

So I think the file.write_all(data)?; below should be good enough to set the file size.

Regarding write() not being supported, I am not sure why that will be. Unless we add F_SEAL_WRITE we should be able to write to the fd location similar to memfd backed files.


// From <linux/memfd.h>
const MFD_CLOEXEC: u32 = 0x0001;
const MFD_SECRET: u32 = 0x0008;
Copy link
Collaborator

Choose a reason for hiding this comment

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

in linux/memfd.h 0x0008 points to MFD_NOEXEC_SEAL

LMK if i'm missing something

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 working on this, added some initial comments.

}

pub struct KeyManager {
pub binding_keys: KeyRegistry,
Copy link
Collaborator

Choose a reason for hiding this comment

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

As discussed today, we need to separate these out into individual Key Management Agents, one for the Workload daemon, and the other for the Key Protection Service.

let meta = KeyMetadata {
id: key_id,
created_at: Instant::now(),
delete_after: Some(Instant::now() + Duration::from_secs(3600)),
Copy link
Collaborator

Choose a reason for hiding this comment

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

The binding key lifespan is a parameter passed down from the orchestration layer.

pub fn generate_binding_keypair(&self) -> (KeyHandle, Vec<u8>) {
clear_stack_on_return(2, || {
let key_id = Uuid::new_v4();
let (pub_key, mut priv_key) = hpke::Kem::X25519HkdfSha256.generate_keypair();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Even though for initial phases, we will exclusively support X25519, we need to make these configurable. The KemAlgorithm will be passed as parameter indicating the DHKEM algorithm to use for the request.

Comment on lines +116 to +120
algo: HpkeAlgorithm {
kem: KemAlgorithm::DhKemX25519HkdfSha256,
kdf: KdfAlgorithm::HkdfSha256,
aead: AeadAlgorithm::Aes256Gcm,
},
Copy link
Collaborator

Choose a reason for hiding this comment

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

Again the exact algorithm chain should be passed from the orchestration layer and not hardcoded here.

Binding { algo: HpkeAlgorithm, binding_public_key: Vec<u8> },
Sek { algo: SigningAlgorithm, verifying_key: Vec<u8> },
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

I would prefer defining the Algorithms below as a common protobuf definition and not in rust. And then use Prost to have a Rust binding for those protos. That would allow us to have a common definition between both the orchestration and the custody core components.

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum KemAlgorithm {
DhKemX25519HkdfSha256 = 1,
Copy link
Collaborator

Choose a reason for hiding this comment

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

For each of these, we should have a default unknown definition, like KEM_ALGORITHM_UNSPECIFIED to prevent accidental values from being set.

let name_cstr = CString::new(name)
.map_err(|_| IoError::new(std::io::ErrorKind::InvalidInput, "Invalid name"))?;
let fd = unsafe {
libc::syscall(libc::SYS_memfd_create, name_cstr.as_ptr(), MFD_CLOEXEC | MFD_SECRET)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah I think I wrote MFD_SECRET in my doc. It should be MFD_NOEXEC_SEAL instead. We should add it to prevent this memory from ever being executable.


pub fn generate_kem_keypair(&self, binding_pub_key: &[u8]) -> (KeyHandle, Vec<u8>) {
clear_stack_on_return(2, || {
let key_id = Uuid::new_v4();
Copy link
Collaborator

Choose a reason for hiding this comment

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

The KEM keys and the Binding keys will share the same UUID. When creating the binding keys, a new UUID will be generated and returned back to the orchestration layer. This same UUID will be passed back to the generate_kem_keypair FFI. So when the KEM keys are generated, these keys are mapped to the same UUID and not a new one. This way we can look up the correct KEM key <-> Binding Key pair during the "decap and seal" and subsequent "open" operation.

@@ -0,0 +1,14 @@

use thiserror::Error;

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 should move this to a protobuf file, to ensure a single definition across the orchestration and custody core layers.


let reaper_handle = Some(thread::spawn(move || {
while !stop_clone.load(Ordering::Relaxed) {
thread::sleep(Duration::from_secs(10));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's make this configurable?

atulpatildbz pushed a commit to atulpatildbz/go-tpm-tools that referenced this pull request Feb 5, 2026
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