diff --git a/keymanager/generate_ffi_headers.sh b/keymanager/generate_ffi_headers.sh index 1d42dfbe8..68a61da81 100755 --- a/keymanager/generate_ffi_headers.sh +++ b/keymanager/generate_ffi_headers.sh @@ -4,6 +4,11 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CBINDGEN_BIN="${CBINDGEN_BIN:-cbindgen}" +"${CBINDGEN_BIN}" --quiet \ + "${ROOT_DIR}/km_common" \ + --crate km_common \ + --config "${ROOT_DIR}/km_common/cbindgen.toml" \ + --output "${ROOT_DIR}/km_common/include/km_common_ffi.h" "${CBINDGEN_BIN}" --quiet \ "${ROOT_DIR}/workload_service/key_custody_core" \ @@ -16,3 +21,4 @@ CBINDGEN_BIN="${CBINDGEN_BIN:-cbindgen}" --crate kps_key_custody_core \ --config "${ROOT_DIR}/key_protection_service/key_custody_core/cbindgen.toml" \ --output "${ROOT_DIR}/key_protection_service/key_custody_core/include/kps_key_custody_core.h" + diff --git a/keymanager/key_protection_service/key_custody_core/cbindgen.toml b/keymanager/key_protection_service/key_custody_core/cbindgen.toml index 9cd011203..9b3b329fa 100644 --- a/keymanager/key_protection_service/key_custody_core/cbindgen.toml +++ b/keymanager/key_protection_service/key_custody_core/cbindgen.toml @@ -8,14 +8,18 @@ style = "type" documentation = false usize_is_size_t = true sys_includes = ["stdbool.h", "stddef.h", "stdint.h"] +includes = ["km_common_ffi.h"] [parse] -parse_deps = false +parse_deps = true +include = ["km_common"] clean = true [export] item_types = ["functions", "structs", "constants", "enums"] -include = ["key_manager_generate_kem_keypair", "key_manager_get_kem_key", "key_manager_destroy_kem_key", "key_manager_enumerate_kem_keys", "KpsKeyInfo", "MAX_ALGORITHM_LEN", "MAX_PUBLIC_KEY_LEN"] - +include = ["key_manager_generate_kem_keypair", "key_manager_get_kem_key", "key_manager_destroy_kem_key", "key_manager_enumerate_kem_keys", "KpsKeyInfo"] +exclude = ["Status", "MAX_ALGORITHM_LEN", "MAX_PUBLIC_KEY_LEN"] +[enum] +prefix_with_name = true diff --git a/keymanager/key_protection_service/key_custody_core/include/kps_key_custody_core.h b/keymanager/key_protection_service/key_custody_core/include/kps_key_custody_core.h index 890aa2205..09d415b3e 100644 --- a/keymanager/key_protection_service/key_custody_core/include/kps_key_custody_core.h +++ b/keymanager/key_protection_service/key_custody_core/include/kps_key_custody_core.h @@ -6,10 +6,7 @@ #include #include #include - -#define MAX_ALGORITHM_LEN 128 - -#define MAX_PUBLIC_KEY_LEN 2048 +#include "km_common_ffi.h" typedef struct { uint8_t uuid[16]; @@ -26,40 +23,40 @@ typedef struct { extern "C" { #endif // __cplusplus -int32_t key_manager_generate_kem_keypair(const uint8_t *algo_ptr, - size_t algo_len, - const uint8_t *binding_pubkey, - size_t binding_pubkey_len, - uint64_t expiry_secs, - uint8_t *out_uuid, - uint8_t *out_pubkey, - size_t out_pubkey_len); +Status key_manager_generate_kem_keypair(const uint8_t *algo_ptr, + size_t algo_len, + const uint8_t *binding_pubkey, + size_t binding_pubkey_len, + uint64_t expiry_secs, + uint8_t *out_uuid, + uint8_t *out_pubkey, + size_t out_pubkey_len); -int32_t key_manager_destroy_kem_key(const uint8_t *uuid_bytes); +Status key_manager_destroy_kem_key(const uint8_t *uuid_bytes); int32_t key_manager_enumerate_kem_keys(KpsKeyInfo *out_entries, size_t max_entries, size_t offset, bool *out_has_more); -int32_t key_manager_decap_and_seal(const uint8_t *uuid_bytes, - const uint8_t *encapsulated_key, - size_t encapsulated_key_len, - const uint8_t *aad, - size_t aad_len, - uint8_t *out_encapsulated_key, - size_t out_encapsulated_key_len, - uint8_t *out_ciphertext, - size_t out_ciphertext_len); +Status key_manager_decap_and_seal(const uint8_t *uuid_bytes, + const uint8_t *encapsulated_key, + size_t encapsulated_key_len, + const uint8_t *aad, + size_t aad_len, + uint8_t *out_encapsulated_key, + size_t out_encapsulated_key_len, + uint8_t *out_ciphertext, + size_t out_ciphertext_len); -int32_t key_manager_get_kem_key(const uint8_t *uuid_bytes, - uint8_t *out_kem_pubkey, - size_t out_kem_pubkey_len, - uint8_t *out_binding_pubkey, - size_t out_binding_pubkey_len, - uint8_t *out_algo, - size_t *out_algo_len, - uint64_t *out_delete_after); +Status key_manager_get_kem_key(const uint8_t *uuid_bytes, + uint8_t *out_kem_pubkey, + size_t out_kem_pubkey_len, + uint8_t *out_binding_pubkey, + size_t out_binding_pubkey_len, + uint8_t *out_algo, + size_t *out_algo_len, + uint64_t *out_remaining_lifespan_secs); #ifdef __cplusplus } // extern "C" diff --git a/keymanager/key_protection_service/key_custody_core/integration_test.go b/keymanager/key_protection_service/key_custody_core/integration_test.go index ad45f656b..1b139aae9 100644 --- a/keymanager/key_protection_service/key_custody_core/integration_test.go +++ b/keymanager/key_protection_service/key_custody_core/integration_test.go @@ -106,7 +106,7 @@ func TestIntegrationGetKEMKeyNotFound(t *testing.T) { t.Fatal("expected error for non-existent UUID") } - expectedErrMsg := "key_manager_get_kem_key failed with code -1" + expectedErrMsg := "FFI status: STATUS_NOT_FOUND" if !strings.Contains(err.Error(), expectedErrMsg) { t.Fatalf("expected error containing %q, got: %v", expectedErrMsg, err) } diff --git a/keymanager/key_protection_service/key_custody_core/kps_key_custody_core_cgo.go b/keymanager/key_protection_service/key_custody_core/kps_key_custody_core_cgo.go index c1dcf0a72..4ade96657 100644 --- a/keymanager/key_protection_service/key_custody_core/kps_key_custody_core_cgo.go +++ b/keymanager/key_protection_service/key_custody_core/kps_key_custody_core_cgo.go @@ -6,6 +6,7 @@ package kpskcc /* +#cgo CFLAGS: -I${SRCDIR}/../../km_common/include #cgo LDFLAGS: -L${SRCDIR}/../../target/release -L${SRCDIR}/../../target/debug -lkps_key_custody_core #cgo LDFLAGS: -lcrypto -lssl #cgo LDFLAGS: -lpthread -ldl -lm -lstdc++ @@ -57,8 +58,8 @@ func GenerateKEMKeypair(algo *keymanager.HpkeAlgorithm, bindingPubKey []byte, li (*C.uint8_t)(unsafe.Pointer(&uuidBytes[0])), (*C.uint8_t)(unsafe.Pointer(&pubkeyBuf[0])), pubkeyLen, - ); rc != 0 { - return uuid.Nil, nil, fmt.Errorf("key_manager_generate_kem_keypair failed with code %d", rc) + ); keymanager.Status(rc) != keymanager.Status_STATUS_SUCCESS { + return uuid.Nil, nil, keymanager.Status(rc).ToStatus() } id, err := uuid.FromBytes(uuidBytes[:]) @@ -91,7 +92,7 @@ func EnumerateKEMKeys(limit, offset int) ([]KEMKeyInfo, bool, error) { &hasMore, ) if rc < 0 { - return nil, false, fmt.Errorf("key_manager_enumerate_kem_keys failed with code %d", rc) + return nil, false, keymanager.Status(-rc).ToStatus() } count := int(rc) @@ -124,12 +125,10 @@ func EnumerateKEMKeys(limit, offset int) ([]KEMKeyInfo, bool, error) { // DestroyKEMKey destroys the KEM key identified by kemUUID via Rust FFI. func DestroyKEMKey(kemUUID uuid.UUID) error { uuidBytes := kemUUID[:] - if rc := C.key_manager_destroy_kem_key( + rc := C.key_manager_destroy_kem_key( (*C.uint8_t)(unsafe.Pointer(&uuidBytes[0])), - ); rc != 0 { - return fmt.Errorf("key_manager_destroy_kem_key failed with code %d", rc) - } - return nil + ) + return keymanager.Status(rc).ToStatus() } // GetKEMKey retrieves KEM and binding public keys, HpkeAlgorithm and remaining lifespan via Rust FFI. @@ -153,8 +152,8 @@ func GetKEMKey(id uuid.UUID) ([]byte, []byte, *keymanager.HpkeAlgorithm, uint64, &algoLenC, &remainingLifespanSecs, ) - if rc != 0 { - return nil, nil, nil, 0, fmt.Errorf("key_manager_get_kem_key failed with code %d", rc) + if keymanager.Status(rc) != keymanager.Status_STATUS_SUCCESS { + return nil, nil, nil, 0, keymanager.Status(rc).ToStatus() } kemPubkey := make([]byte, len(kemPubkeyBuf)) @@ -201,8 +200,8 @@ func DecapAndSeal(kemUUID uuid.UUID, encapsulatedKey, aad []byte) ([]byte, []byt outEncKeyLen, (*C.uint8_t)(unsafe.Pointer(&outCT[0])), outCTLen, - ); rc != 0 { - return nil, nil, fmt.Errorf("key_manager_decap_and_seal failed with code %d", rc) + ); keymanager.Status(rc) != keymanager.Status_STATUS_SUCCESS { + return nil, nil, keymanager.Status(rc).ToStatus() } sealEnc := make([]byte, outEncKeyLen) diff --git a/keymanager/key_protection_service/key_custody_core/src/lib.rs b/keymanager/key_protection_service/key_custody_core/src/lib.rs index ae6e820d5..721e9fc02 100644 --- a/keymanager/key_protection_service/key_custody_core/src/lib.rs +++ b/keymanager/key_protection_service/key_custody_core/src/lib.rs @@ -1,6 +1,8 @@ -use km_common::algorithms::HpkeAlgorithm; use km_common::crypto::PublicKey; use km_common::key_types::{KeyRecord, KeyRegistry, KeySpec}; +use km_common::proto::{HpkeAlgorithm, Status}; +use km_common::{MAX_ALGORITHM_LEN, MAX_PUBLIC_KEY_LEN}; + use prost::Message; use std::slice; use std::sync::Arc; @@ -20,22 +22,17 @@ fn generate_kem_keypair_internal( algo: HpkeAlgorithm, binding_pubkey: PublicKey, expiry_secs: u64, -) -> Result<(uuid::Uuid, PublicKey), i32> { - let result = - KeyRecord::create_bound_kem_key(algo, binding_pubkey, Duration::from_secs(expiry_secs)); - - match result { - Ok(record) => { - let id = record.meta.id; - let pubkey = match &record.meta.spec { - KeySpec::KemWithBindingPub { kem_public_key, .. } => kem_public_key.clone(), - _ => return Err(-1), - }; - KEY_REGISTRY.add_key(record); - Ok((id, pubkey)) - } - Err(_) => Err(-1), - } +) -> Result<(uuid::Uuid, PublicKey), Status> { + let record = + KeyRecord::create_bound_kem_key(algo, binding_pubkey, Duration::from_secs(expiry_secs))?; + + let id = record.meta.id; + let pubkey = match &record.meta.spec { + KeySpec::KemWithBindingPub { kem_public_key, .. } => kem_public_key.clone(), + _ => return Err(Status::InternalError), + }; + KEY_REGISTRY.add_key(record); + Ok((id, pubkey)) } /// Generates a new KEM keypair associated with a binding public key. @@ -60,9 +57,10 @@ fn generate_kem_keypair_internal( /// * `out_pubkey_len` is either null or points to a valid `usize`. /// /// ## Returns -/// * `0` on success. -/// * `-1` if an error occurred during key generation or if `binding_pubkey` is null/empty. -/// * `-2` if the `out_pubkey` buffer size does not match the key size. +/// * `Status::Success` on success. +/// * `Status::InvalidArgument` if `binding_pubkey` or `algo_ptr` is null/empty. +/// * `Status::InvalidArgument` if the `out_pubkey` buffer size does not match the key size. +/// * Other `Status` values on failure. #[unsafe(no_mangle)] pub unsafe extern "C" fn key_manager_generate_kem_keypair( @@ -74,9 +72,9 @@ pub unsafe extern "C" fn key_manager_generate_kem_keypair( out_uuid: *mut u8, out_pubkey: *mut u8, out_pubkey_len: usize, -) -> i32 { - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - // Safety Invariant Checks +) -> Status { + km_common::ffi_call(|| { + // Convert to Safe Types if binding_pubkey.is_null() || binding_pubkey_len == 0 || out_pubkey.is_null() @@ -84,7 +82,7 @@ pub unsafe extern "C" fn key_manager_generate_kem_keypair( || algo_ptr.is_null() || algo_len == 0 { - return -1; + return Err(Status::InvalidArgument); } // Convert to Safe Types @@ -94,30 +92,18 @@ pub unsafe extern "C" fn key_manager_generate_kem_keypair( let out_uuid = unsafe { slice::from_raw_parts_mut(out_uuid, 16) }; let out_pubkey = unsafe { slice::from_raw_parts_mut(out_pubkey, out_pubkey_len) }; - let binding_pubkey = match PublicKey::try_from(binding_pubkey_slice.to_vec()) { - Ok(pk) => pk, - Err(_) => return -1, - }; - - let algo = match HpkeAlgorithm::decode(algo_slice) { - Ok(a) => a, - Err(_) => return -1, - }; + let binding_pubkey = PublicKey::try_from(binding_pubkey_slice.to_vec())?; + let algo = HpkeAlgorithm::decode(algo_slice).map_err(|_| Status::InvalidArgument)?; // Call Safe Internal Function - match generate_kem_keypair_internal(algo, binding_pubkey, expiry_secs) { - Ok((id, pubkey)) => { - if out_pubkey_len != pubkey.as_bytes().len() { - return -2; - } - out_uuid.copy_from_slice(id.as_bytes()); - out_pubkey.copy_from_slice(pubkey.as_bytes()); - 0 // Success - } - Err(e) => e, + let (id, pubkey) = generate_kem_keypair_internal(algo, binding_pubkey, expiry_secs)?; + if out_pubkey_len != pubkey.as_bytes().len() { + return Err(Status::InvalidArgument); } - })) - .unwrap_or(-1) + out_uuid.copy_from_slice(id.as_bytes()); + out_pubkey.copy_from_slice(pubkey.as_bytes()); + Ok(()) + }) } /// Destroys the KEM key associated with the given UUID. @@ -130,13 +116,14 @@ pub unsafe extern "C" fn key_manager_generate_kem_keypair( /// The caller must ensure that `uuid_bytes` points to a valid 16-byte buffer. /// /// ## Returns -/// * `0` on success. -/// * `-1` if the UUID pointer is null or the key was not found. +/// * `Status::Success` on success. +/// * `Status::InvalidArgument` if the UUID pointer is null. +/// * `Status::NotFound` if the key was not found. #[unsafe(no_mangle)] -pub unsafe extern "C" fn key_manager_destroy_kem_key(uuid_bytes: *const u8) -> i32 { - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { +pub unsafe extern "C" fn key_manager_destroy_kem_key(uuid_bytes: *const u8) -> Status { + km_common::ffi_call(|| { if uuid_bytes.is_null() { - return -1; + return Err(Status::InvalidArgument); } let uuid = unsafe { let mut bytes = [0u8; 16]; @@ -144,12 +131,9 @@ pub unsafe extern "C" fn key_manager_destroy_kem_key(uuid_bytes: *const u8) -> i Uuid::from_bytes(bytes) }; - match KEY_REGISTRY.remove_key(&uuid) { - Some(_) => 0, // Success - None => -1, // Not found - } - })) - .unwrap_or(-1) + KEY_REGISTRY.remove_key(&uuid).ok_or(Status::NotFound)?; + Ok(()) + }) } /// Internal function to decapsulate and reseal a shared secret. @@ -159,10 +143,10 @@ fn decap_and_seal_internal( aad: &[u8], out_encapsulated_key: &mut [u8], out_ciphertext: &mut [u8], -) -> Result<(), i32> { +) -> Result<(), Status> { // Get key record from registry let Some(key_record) = KEY_REGISTRY.get_key(&uuid) else { - Err(-1)? // Key not found + Err(Status::NotFound)? // Key not found }; let KeySpec::KemWithBindingPub { @@ -171,34 +155,25 @@ fn decap_and_seal_internal( .. } = &key_record.meta.spec else { - Err(-1)? // Invalid key type + Err(Status::InternalError)? // Invalid key type }; let priv_key = key_record.get_private_key(); // Decapsulate - let shared_secret = match km_common::crypto::decaps(&priv_key, encapsulated_key) { - Ok(s) => s, - Err(_) => return Err(-3), - }; + let shared_secret = km_common::crypto::decaps(&priv_key, encapsulated_key)?; // Seal - match km_common::crypto::hpke_seal(binding_public_key, &shared_secret, aad, hpke_algo) { - Ok((enc, ct)) => { - if out_encapsulated_key.len() != enc.len() || out_ciphertext.len() != ct.len() { - return Err(-2); - } - out_encapsulated_key.copy_from_slice(&enc); - out_ciphertext.copy_from_slice(&ct); - Ok(()) - } - Err(_) => Err(-4), + let (enc, ct) = + km_common::crypto::hpke_seal(binding_public_key, &shared_secret, aad, hpke_algo)?; + if out_encapsulated_key.len() != enc.len() || out_ciphertext.len() != ct.len() { + return Err(Status::InvalidArgument); } + out_encapsulated_key.copy_from_slice(&enc); + out_ciphertext.copy_from_slice(&ct); + Ok(()) } -pub const MAX_ALGORITHM_LEN: usize = 128; -pub const MAX_PUBLIC_KEY_LEN: usize = 2048; - #[repr(C)] pub struct KpsKeyInfo { pub uuid: [u8; 16], @@ -229,7 +204,7 @@ impl Default for KpsKeyInfo { fn enumerate_kem_keys_internal( entries: &mut [KpsKeyInfo], offset: usize, -) -> Result<(usize, bool), i32> { +) -> Result<(usize, bool), Status> { let (metas, total_count) = KEY_REGISTRY.list_all_keys(offset, entries.len()); let count = metas.len(); let has_more = offset + count < total_count; @@ -242,7 +217,7 @@ fn enumerate_kem_keys_internal( .. } = &meta.spec else { - return Err(-1); // Implementation error, KPS should only contain KEM keys. + return Err(Status::InternalError); // Implementation error, KPS should only contain KEM keys. }; let algo_bytes = algo.encode_to_vec(); @@ -254,7 +229,7 @@ fn enumerate_kem_keys_internal( algo_bytes.len(), pub_key.as_bytes().len() ); - return Err(-2); // Buffer Limit Exceeded + return Err(Status::InternalError); // Buffer Limit Exceeded } if binding_pub_key.as_bytes().len() > MAX_PUBLIC_KEY_LEN { debug_assert!( @@ -262,7 +237,7 @@ fn enumerate_kem_keys_internal( "Implementation error: Binding Key size exceeds buffer limits! (bpk={})", binding_pub_key.as_bytes().len() ); - return Err(-2); + return Err(Status::InternalError); } let now = Instant::now(); @@ -295,24 +270,19 @@ pub unsafe extern "C" fn key_manager_enumerate_kem_keys( offset: usize, out_has_more: Option<&mut bool>, ) -> i32 { - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + km_common::ffi_call_i32(|| { if out_entries.is_null() { - return -1; + return Err(Status::InvalidArgument); } let entries = unsafe { slice::from_raw_parts_mut(out_entries, max_entries) }; - match enumerate_kem_keys_internal(entries, offset) { - Ok((count, has_more)) => { - if let Some(has_more_ref) = out_has_more { - *has_more_ref = has_more; - } - count as i32 - } - Err(e) => e, + let (count, has_more) = enumerate_kem_keys_internal(entries, offset)?; + if let Some(has_more_ref) = out_has_more { + *has_more_ref = has_more; } - })) - .unwrap_or(-1) + Ok(count as i32) + }) } /// Decapsulates a shared secret using a stored KEM key and immediately reseals it using the associated binding public key. @@ -337,11 +307,10 @@ pub unsafe extern "C" fn key_manager_enumerate_kem_keys( /// * `out_ciphertext` points to a valid buffer of `out_ciphertext_len` bytes. /// /// ## Returns -/// * `0` on success. -/// * `-1` if arguments are invalid or key is not found. -/// * `-2` if output buffers are too small. -/// * `-3` if decapsulation fails. -/// * `-4` if sealing (HPKE encryption) fails. +/// * `Status::Success` on success. +/// * `Status::InvalidArgument` if arguments are invalid. +/// * `Status::NotFound` if key is not found. +/// * Other `Status` values on failure. #[unsafe(no_mangle)] pub unsafe extern "C" fn key_manager_decap_and_seal( uuid_bytes: *const u8, @@ -353,8 +322,8 @@ pub unsafe extern "C" fn key_manager_decap_and_seal( out_encapsulated_key_len: usize, out_ciphertext: *mut u8, out_ciphertext_len: usize, -) -> i32 { - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { +) -> Status { + km_common::ffi_call(|| { if uuid_bytes.is_null() || encapsulated_key.is_null() || encapsulated_key_len == 0 @@ -363,7 +332,7 @@ pub unsafe extern "C" fn key_manager_decap_and_seal( || out_ciphertext.is_null() || out_ciphertext_len == 0 { - return -1; + return Err(Status::InvalidArgument); } // Convert to Safe Types @@ -380,29 +349,22 @@ pub unsafe extern "C" fn key_manager_decap_and_seal( let out_ciphertext_slice = unsafe { slice::from_raw_parts_mut(out_ciphertext, out_ciphertext_len) }; - let uuid = match Uuid::from_slice(uuid_slice) { - Ok(u) => u, - Err(_) => return -1, - }; + let uuid = Uuid::from_slice(uuid_slice).map_err(|_| Status::InvalidArgument)?; // Call Safe Internal Function - match decap_and_seal_internal( + decap_and_seal_internal( uuid, enc_key_slice, aad_slice, out_encapsulated_key_slice, out_ciphertext_slice, - ) { - Ok(_) => 0, // Success - Err(e) => e, - } - })) - .unwrap_or(-1) + ) + }) } /// Internal function to retrieve a KEM key's public keys and expiration. -fn get_kem_key_internal(uuid: Uuid) -> Result<(HpkeAlgorithm, PublicKey, PublicKey, u64), i32> { - let record = KEY_REGISTRY.get_key(&uuid).ok_or(-1)?; +fn get_kem_key_internal(uuid: Uuid) -> Result<(HpkeAlgorithm, PublicKey, PublicKey, u64), Status> { + let record = KEY_REGISTRY.get_key(&uuid).ok_or(Status::NotFound)?; match &record.meta.spec { KeySpec::KemWithBindingPub { algo, @@ -421,7 +383,7 @@ fn get_kem_key_internal(uuid: Uuid) -> Result<(HpkeAlgorithm, PublicKey, PublicK remaining.as_secs(), )) } - _ => Err(-1), + _ => Err(Status::InternalError), } } @@ -441,9 +403,9 @@ fn get_kem_key_internal(uuid: Uuid) -> Result<(HpkeAlgorithm, PublicKey, PublicK /// This function is unsafe because it dereferences raw pointers. /// /// ## Returns -/// * `0` on success. -/// * `-1` if arguments are invalid or key is not found. -/// * `-2` if any output buffer is too small. +/// * `Status::Success` on success. +/// * `Status::InvalidArgument` if arguments are invalid. +/// * `Status::NotFound` if key is not found. #[unsafe(no_mangle)] pub unsafe extern "C" fn key_manager_get_kem_key( uuid_bytes: *const u8, @@ -454,8 +416,8 @@ pub unsafe extern "C" fn key_manager_get_kem_key( out_algo: *mut u8, out_algo_len: *mut usize, out_remaining_lifespan_secs: *mut u64, -) -> i32 { - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { +) -> Status { + km_common::ffi_call(|| { if uuid_bytes.is_null() || out_kem_pubkey.is_null() || out_kem_pubkey_len == 0 @@ -465,7 +427,7 @@ pub unsafe extern "C" fn key_manager_get_kem_key( || out_algo_len.is_null() || out_remaining_lifespan_secs.is_null() { - return -1; + return Err(Status::InvalidArgument); } // Convert to Safe Types first @@ -478,39 +440,31 @@ pub unsafe extern "C" fn key_manager_get_kem_key( let out_algo_len_ref = unsafe { &mut *out_algo_len }; let out_algo_slice = unsafe { std::slice::from_raw_parts_mut(out_algo, *out_algo_len_ref) }; - let uuid = match Uuid::from_slice(uuid_slice) { - Ok(u) => u, - Err(_) => return -1, - }; + let uuid = Uuid::from_slice(uuid_slice).map_err(|_| Status::InvalidArgument)?; // Call Safe Internal Function - match get_kem_key_internal(uuid) { - Ok((algo, kem_pubkey, binding_pubkey, remaining_secs)) => { - let algo_bytes = algo.encode_to_vec(); - if out_kem_pubkey_slice.len() != kem_pubkey.as_bytes().len() - || out_binding_pubkey_slice.len() != binding_pubkey.as_bytes().len() - || *out_algo_len_ref < algo_bytes.len() - { - return -2; - } - - out_kem_pubkey_slice.copy_from_slice(kem_pubkey.as_bytes()); - out_binding_pubkey_slice.copy_from_slice(binding_pubkey.as_bytes()); - out_algo_slice[..algo_bytes.len()].copy_from_slice(&algo_bytes); - *out_algo_len_ref = algo_bytes.len(); - *out_remaining_lifespan_secs_ref = remaining_secs; - 0 // Success - } - Err(e) => e, + let (algo, kem_pubkey, binding_pubkey, remaining_secs) = get_kem_key_internal(uuid)?; + let algo_bytes = algo.encode_to_vec(); + if out_kem_pubkey_slice.len() != kem_pubkey.as_bytes().len() + || out_binding_pubkey_slice.len() != binding_pubkey.as_bytes().len() + || *out_algo_len_ref < algo_bytes.len() + { + return Err(Status::InvalidArgument); } - })) - .unwrap_or(-1) + + out_kem_pubkey_slice.copy_from_slice(kem_pubkey.as_bytes()); + out_binding_pubkey_slice.copy_from_slice(binding_pubkey.as_bytes()); + out_algo_slice[..algo_bytes.len()].copy_from_slice(&algo_bytes); + *out_algo_len_ref = algo_bytes.len(); + *out_remaining_lifespan_secs_ref = remaining_secs; + Ok(()) + }) } #[cfg(test)] mod tests { use super::*; - use km_common::algorithms::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm}; + use km_common::proto::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm}; use prost::Message; struct KeyCleanup(Uuid); @@ -569,7 +523,7 @@ mod tests { ) }; - assert_eq!(result, 0); + assert_eq!(result, Status::Success); let _cleanup = KeyCleanup(Uuid::from_bytes(uuid_bytes)); assert_ne!(uuid_bytes, [0u8; 16]); @@ -599,7 +553,7 @@ mod tests { ) }; - assert_eq!(result, -1); + assert_eq!(result, Status::InvalidArgument); assert_eq!(uuid_bytes, [0u8; 16]); } @@ -629,7 +583,7 @@ mod tests { ) }; - assert_eq!(result, -2); + assert_eq!(result, Status::InvalidArgument); assert_eq!(uuid_bytes, [0u8; 16]); // Should remain untouched/zero assert_eq!(&pubkey_bytes[..32], &[0u8; 32]); // Should remain untouched/zero } @@ -657,7 +611,7 @@ mod tests { ) }; - assert_eq!(result, -1); + assert_eq!(result, Status::InvalidArgument); } #[test] @@ -684,7 +638,7 @@ mod tests { ) }; - assert_eq!(result, -1); + assert_eq!(result, Status::InvalidArgument); } #[test] @@ -710,35 +664,35 @@ mod tests { pubkey_bytes.as_mut_ptr(), pubkey_len, ); - assert_eq!(res, 0); + assert_eq!(res, Status::Success); } let _cleanup = KeyCleanup(Uuid::from_bytes(uuid_bytes)); let result = unsafe { key_manager_destroy_kem_key(uuid_bytes.as_ptr()) }; - assert_eq!(result, 0); + assert_eq!(result, Status::Success); // Second destroy should fail let result = unsafe { key_manager_destroy_kem_key(uuid_bytes.as_ptr()) }; - assert_eq!(result, -1); + assert_eq!(result, Status::NotFound.into()); } #[test] fn test_destroy_kem_key_not_found() { let uuid_bytes = [0u8; 16]; let result = unsafe { key_manager_destroy_kem_key(uuid_bytes.as_ptr()) }; - assert_eq!(result, -1); + assert_eq!(result, Status::NotFound.into()); } #[test] fn test_destroy_kem_key_null_ptr() { let result = unsafe { key_manager_destroy_kem_key(std::ptr::null()) }; - assert_eq!(result, -1); + assert_eq!(result, Status::InvalidArgument); } #[test] fn test_enumerate_kem_keys_null_pointers() { let result = unsafe { key_manager_enumerate_kem_keys(std::ptr::null_mut(), 10, 0, None) }; - assert_eq!(result, -1); + assert_eq!(result, -(Status::InvalidArgument as i32)); } #[test] @@ -768,7 +722,7 @@ mod tests { pubkey_len, ) }; - assert_eq!(rc, 0); + assert_eq!(rc, Status::Success); let _cleanup = KeyCleanup(Uuid::from_bytes(uuid_bytes)); // Enumerate. @@ -938,7 +892,7 @@ mod tests { ) }; - assert_eq!(result, 0); + assert_eq!(result, Status::Success); // 4. Verify we can decrypt the result using binding_sk let recovered_shared_secret = @@ -977,7 +931,7 @@ mod tests { ) }; - assert_eq!(result, -1); + assert_eq!(result, Status::NotFound.into()); } #[test] @@ -999,7 +953,7 @@ mod tests { ) }; - assert_eq!(result, -1); + assert_eq!(result, Status::InvalidArgument); } #[test] @@ -1030,7 +984,7 @@ mod tests { kem_pubkey_len, ) }; - assert_eq!(res, 0); + assert_eq!(res, Status::Success); let _cleanup = KeyCleanup(Uuid::from_bytes(uuid_bytes)); // 3. Call with invalid encapsulated key (wrong length for X25519) @@ -1053,7 +1007,7 @@ mod tests { ) }; - assert_eq!(result, -3); + assert_eq!(result, Status::DecapsulationFailure.into()); } #[test] @@ -1085,7 +1039,11 @@ mod tests { kem_pubkey_len, ) }; - assert_eq!(res, 0, "Setup failed: key generation returned error"); + assert_eq!( + res, + Status::Success, + "Setup failed: key generation returned error" + ); let _cleanup = KeyCleanup(Uuid::from_bytes(uuid_bytes)); // 3. Generate valid client encapsulation @@ -1113,7 +1071,7 @@ mod tests { ) }; - assert_eq!(result, -2); + assert_eq!(result, Status::InvalidArgument); } #[test] @@ -1142,7 +1100,7 @@ mod tests { pubkey_len, ) }; - assert_eq!(res, 0); + assert_eq!(res, Status::Success); let _cleanup = KeyCleanup(Uuid::from_bytes(uuid_bytes)); // Now, retrieve it. @@ -1165,7 +1123,7 @@ mod tests { ) }; - assert_eq!(result, 0); + assert_eq!(result, Status::Success); assert_eq!(generated_kem_pubkey_bytes, retrieved_kem_pubkey_bytes); assert_eq!(binding_pubkey, retrieved_binding_pubkey_bytes); assert_eq!(algo_bytes, &retrieved_algo_bytes[..retrieved_algo_len]); @@ -1194,7 +1152,7 @@ mod tests { &mut remaining_lifespan_secs, ) }; - assert_eq!(result, -1); + assert_eq!(result, Status::NotFound.into()); } #[test] @@ -1223,7 +1181,7 @@ mod tests { pubkey_len, ) }; - assert_eq!(res, 0); + assert_eq!(res, Status::Success); let _cleanup = KeyCleanup(Uuid::from_bytes(uuid_bytes)); // Now, retrieve it with invalid buffer lengths. @@ -1246,7 +1204,7 @@ mod tests { &mut remaining_lifespan_secs, ) }; - assert_eq!(result, -2); + assert_eq!(result, Status::InvalidArgument); // Binding pubkey buffer too small. let result = unsafe { @@ -1261,6 +1219,6 @@ mod tests { &mut remaining_lifespan_secs, ) }; - assert_eq!(result, -2); + assert_eq!(result, Status::InvalidArgument.into()); } } diff --git a/keymanager/km_common/build.rs b/keymanager/km_common/build.rs index 360060e19..97ff0a9ea 100644 --- a/keymanager/km_common/build.rs +++ b/keymanager/km_common/build.rs @@ -8,6 +8,9 @@ fn main() -> Result<()> { let mut config = prost_build::Config::new(); + // Output the generated code to src/ so cbindgen can find it. + config.out_dir("src"); + config.compile_protos( &["proto/algorithms.proto", "proto/status.proto"], &["proto/"], diff --git a/keymanager/km_common/cbindgen.toml b/keymanager/km_common/cbindgen.toml new file mode 100644 index 000000000..847c607ad --- /dev/null +++ b/keymanager/km_common/cbindgen.toml @@ -0,0 +1,17 @@ +language = "C" +include_guard = "KM_COMMON_FFI_H_" +pragma_once = false +autogen_warning = "/* Auto-generated by cbindgen. DO NOT EDIT. */" +cpp_compat = true +no_includes = true +style = "type" +documentation = false +usize_is_size_t = true +sys_includes = ["stdint.h"] + +[export] +item_types = ["enums", "constants"] +include = ["Status", "MAX_ALGORITHM_LEN", "MAX_PUBLIC_KEY_LEN"] + +[enum] +prefix_with_name = true diff --git a/keymanager/km_common/include/km_common_ffi.h b/keymanager/km_common/include/km_common_ffi.h new file mode 100644 index 000000000..9821310f0 --- /dev/null +++ b/keymanager/km_common/include/km_common_ffi.h @@ -0,0 +1,36 @@ +#ifndef KM_COMMON_FFI_H_ +#define KM_COMMON_FFI_H_ + +/* Auto-generated by cbindgen. DO NOT EDIT. */ + +#include + +#define MAX_ALGORITHM_LEN 128 + +#define MAX_PUBLIC_KEY_LEN 2048 + +enum Status +#ifdef __cplusplus + : int32_t +#endif // __cplusplus + { + Status_Unspecified = 0, + Status_Success = 1, + Status_InternalError = 2, + Status_InvalidArgument = 3, + Status_NotFound = 4, + Status_AlreadyExists = 5, + Status_PermissionDenied = 6, + Status_Unauthenticated = 7, + Status_UnsupportedAlgorithm = 8, + Status_InvalidKey = 9, + Status_CryptoError = 10, + Status_DecryptionFailure = 11, + Status_EncryptionFailure = 12, + Status_DecapsulationFailure = 13, +}; +#ifndef __cplusplus +typedef int32_t Status; +#endif // __cplusplus + +#endif /* KM_COMMON_FFI_H_ */ diff --git a/keymanager/km_common/proto/algorithms.proto b/keymanager/km_common/proto/algorithms.proto index 63e58a280..154b0ea15 100644 --- a/keymanager/km_common/proto/algorithms.proto +++ b/keymanager/km_common/proto/algorithms.proto @@ -32,7 +32,7 @@ message HpkeAlgorithm { // Specifies parameters for a particular algorithm type. message AlgorithmParams { oneof params { - // KEM algorithm identifier (e.g., DHKEM_X25519_HKDF_SHA256). + // KEM algorithm identifier (e.g., KEM_ALGORITHM_DHKEM_X25519_HKDF_SHA256). KemAlgorithm kem_id = 1; } } diff --git a/keymanager/km_common/proto/status.go b/keymanager/km_common/proto/status.go index de68d94b5..6ed069db4 100644 --- a/keymanager/km_common/proto/status.go +++ b/keymanager/km_common/proto/status.go @@ -14,8 +14,11 @@ func (e *FFIStatus) Error() string { return fmt.Sprintf("FFI status: %s", e.Code.String()) } -// Is allows users to check errors.Is(err, Status_CODE.ToStatus()) or errors.Is(err, &FFIStatus{Code: Status_CODE}). +// Is allows users to check errors.Is(err, Status_CODE) or errors.Is(err, &FFIStatus{Code: Status_CODE}). func (e *FFIStatus) Is(target error) bool { + if t, ok := target.(Status); ok { + return e.Code == t + } if t, ok := target.(*FFIStatus); ok { return e.Code == t.Code } @@ -27,6 +30,11 @@ func (e Status) Status() string { return e.String() } +// Error allows Status to be used as a Go error for comparison in errors.Is. +func (e Status) Error() string { + return e.String() +} + // ToStatus converts a Status to a Go error, or returns nil if it is Success. func (e Status) ToStatus() error { if e == Status_STATUS_SUCCESS { diff --git a/keymanager/km_common/proto/status.proto b/keymanager/km_common/proto/status.proto index ec2cce582..b1ac0ac71 100644 --- a/keymanager/km_common/proto/status.proto +++ b/keymanager/km_common/proto/status.proto @@ -5,18 +5,18 @@ package keymanager; option go_package = "github.com/google/go-tpm-tools/keymanager/km_common/proto;keymanager"; enum Status { - STATUS_UNSPECIFIED = 0; - STATUS_SUCCESS = 1; - STATUS_INTERNAL_ERROR = 2; - STATUS_INVALID_ARGUMENT = 3; - STATUS_NOT_FOUND = 4; - STATUS_ALREADY_EXISTS = 5; - STATUS_PERMISSION_DENIED = 6; - STATUS_UNAUTHENTICATED = 7; + STATUS_UNSPECIFIED = 0; + STATUS_SUCCESS = 1; + STATUS_INTERNAL_ERROR = 2; + STATUS_INVALID_ARGUMENT = 3; + STATUS_NOT_FOUND = 4; + STATUS_ALREADY_EXISTS = 5; + STATUS_PERMISSION_DENIED = 6; + STATUS_UNAUTHENTICATED = 7; STATUS_UNSUPPORTED_ALGORITHM = 8; - STATUS_INVALID_KEY = 9; - STATUS_CRYPTO_ERROR = 10; - STATUS_DECRYPTION_FAILURE = 11; - STATUS_ENCRYPTION_FAILURE = 12; + STATUS_INVALID_KEY = 9; + STATUS_CRYPTO_ERROR = 10; + STATUS_DECRYPTION_FAILURE = 11; + STATUS_ENCRYPTION_FAILURE = 12; STATUS_DECAPSULATION_FAILURE = 13; } diff --git a/keymanager/km_common/proto/status_test.go b/keymanager/km_common/proto/status_test.go index 3e74f8cb7..d7a14a09c 100644 --- a/keymanager/km_common/proto/status_test.go +++ b/keymanager/km_common/proto/status_test.go @@ -10,11 +10,13 @@ func TestFFIStatus(t *testing.T) { if err == nil { t.Fatalf("ToStatus() = nil, want error") } - if !errors.Is(err, Status_STATUS_INTERNAL_ERROR.ToStatus()) { - t.Errorf("errors.Is(err, Status_STATUS_INTERNAL_ERROR.ToStatus()) = false, want true") + + // Test simplified comparison + if !errors.Is(err, Status_STATUS_INTERNAL_ERROR) { + t.Errorf("errors.Is(err, Status_STATUS_INTERNAL_ERROR) = false, want true") } - if errors.Is(err, Status_STATUS_INVALID_ARGUMENT.ToStatus()) { - t.Errorf("errors.Is(err, Status_STATUS_INVALID_ARGUMENT.ToStatus()) = true, want false") + if errors.Is(err, Status_STATUS_INVALID_ARGUMENT) { + t.Errorf("errors.Is(err, Status_STATUS_INVALID_ARGUMENT) = true, want false") } // Test with FFIStatus pointer diff --git a/keymanager/km_common/src/crypto.rs b/keymanager/km_common/src/crypto.rs index 2809b2be6..1539c47a5 100644 --- a/keymanager/km_common/src/crypto.rs +++ b/keymanager/km_common/src/crypto.rs @@ -1,8 +1,7 @@ -use crate::algorithms::{HpkeAlgorithm, KemAlgorithm}; +use crate::proto::{Status, HpkeAlgorithm, KemAlgorithm}; pub mod secret_box; use crate::crypto::secret_box::SecretBox; use clear_on_drop::clear_stack_on_return; -use thiserror::Error; mod x25519; pub use x25519::{X25519PrivateKey, X25519PublicKey}; @@ -19,7 +18,7 @@ pub(crate) trait PublicKeyOps: Send + Sync { plaintext: &SecretBox, aad: &[u8], algo: &HpkeAlgorithm, - ) -> Result<(Vec, Vec), Error>; + ) -> Result<(Vec, Vec), Status>; #[cfg(any(test, feature = "test-utils"))] /// Performs DHKEM encapsulation operation. @@ -31,7 +30,7 @@ pub(crate) trait PublicKeyOps: Send + Sync { fn encap_internal( &self, ephemeral_sk: Option<&PrivateKey>, - ) -> Result<(SecretBox, Vec), Error>; + ) -> Result<(SecretBox, Vec), Status>; /// Returns the raw bytes of the public key. fn as_bytes(&self) -> &[u8]; @@ -42,7 +41,7 @@ pub(crate) trait PrivateKeyOps: Send + Sync { /// Decapsulates the shared secret from an encapsulated key. /// /// Returns the decapsulated shared secret as a `SecretBox`. - fn decaps_internal(&self, enc: &[u8]) -> Result; + fn decaps_internal(&self, enc: &[u8]) -> Result; /// Decrypts a ciphertext using HPKE. /// @@ -53,7 +52,7 @@ pub(crate) trait PrivateKeyOps: Send + Sync { ciphertext: &[u8], aad: &[u8], algo: &HpkeAlgorithm, - ) -> Result; + ) -> Result; } /// A wrapper enum for different public key types. @@ -72,10 +71,10 @@ impl PublicKey { } impl TryFrom> for PublicKey { - type Error = Error; + type Error = Status; fn try_from(value: Vec) -> Result { - let bytes: [u8; 32] = value.try_into().map_err(|_| Error::KeyLenMismatch)?; + let bytes: [u8; 32] = value.try_into().map_err(|_| Status::InvalidArgument)?; Ok(PublicKey::X25519(X25519PublicKey(bytes))) } } @@ -86,7 +85,7 @@ impl PublicKeyOps for PublicKey { plaintext: &SecretBox, aad: &[u8], algo: &HpkeAlgorithm, - ) -> Result<(Vec, Vec), Error> { + ) -> Result<(Vec, Vec), Status> { match self { PublicKey::X25519(pk) => pk.hpke_seal_internal(plaintext, aad, algo), } @@ -96,7 +95,7 @@ impl PublicKeyOps for PublicKey { fn encap_internal( &self, ephemeral_sk: Option<&PrivateKey>, - ) -> Result<(SecretBox, Vec), Error> { + ) -> Result<(SecretBox, Vec), Status> { match self { PublicKey::X25519(pk) => pk.encap_internal(ephemeral_sk), } @@ -127,7 +126,7 @@ impl From for SecretBox { } impl PrivateKeyOps for PrivateKey { - fn decaps_internal(&self, enc: &[u8]) -> Result { + fn decaps_internal(&self, enc: &[u8]) -> Result { match self { PrivateKey::X25519(sk) => sk.decaps_internal(enc), } @@ -139,41 +138,23 @@ impl PrivateKeyOps for PrivateKey { ciphertext: &[u8], aad: &[u8], algo: &HpkeAlgorithm, - ) -> Result { + ) -> Result { match self { PrivateKey::X25519(sk) => sk.hpke_open_internal(enc, ciphertext, aad, algo), } } } -#[derive(Debug, Error)] -pub enum Error { - #[error("Key length mismatch")] - KeyLenMismatch, - #[error("Decapsulation error")] - DecapsError, - #[error("HPKE decryption error")] - HpkeDecryptionError, - #[error("HPKE encryption error")] - HpkeEncryptionError, - #[error("Unsupported algorithm")] - UnsupportedAlgorithm, - #[error("Invalid key")] - InvalidKey, - #[error("Crypto library error")] - CryptoError, -} - /// Generates a keypair for the given KEM algorithm. /// /// Returns a tuple containing the public and private keys respectively. -pub fn generate_keypair(algo: KemAlgorithm) -> Result<(PublicKey, PrivateKey), Error> { +pub fn generate_keypair(algo: KemAlgorithm) -> Result<(PublicKey, PrivateKey), Status> { clear_stack_on_return(CLEAR_STACK_PAGES, || match algo { KemAlgorithm::DhkemX25519HkdfSha256 => { let (pk, sk) = x25519::generate_keypair(); Ok((PublicKey::X25519(pk), PrivateKey::X25519(sk))) } - _ => Err(Error::UnsupportedAlgorithm), + _ => Err(Status::UnsupportedAlgorithm), }) } @@ -181,14 +162,14 @@ pub fn generate_keypair(algo: KemAlgorithm) -> Result<(PublicKey, PrivateKey), E /// /// Returns the shared secret as a `SecretBox` and the encapsulated key respectively. #[cfg(any(test, feature = "test-utils"))] -pub fn encap(pub_key: &PublicKey) -> Result<(SecretBox, Vec), Error> { +pub fn encap(pub_key: &PublicKey) -> Result<(SecretBox, Vec), Status> { clear_stack_on_return(CLEAR_STACK_PAGES, || pub_key.encap_internal(None)) } /// Decapsulates the shared secret from an encapsulated key using the specified private key. /// /// Returns the decapsulated shared secret as a `SecretBox`. -pub fn decaps(priv_key: &PrivateKey, enc: &[u8]) -> Result { +pub fn decaps(priv_key: &PrivateKey, enc: &[u8]) -> Result { clear_stack_on_return(CLEAR_STACK_PAGES, || priv_key.decaps_internal(enc)) } @@ -201,7 +182,7 @@ pub fn hpke_open( ciphertext: &[u8], aad: &[u8], algo: &HpkeAlgorithm, -) -> Result { +) -> Result { clear_stack_on_return(CLEAR_STACK_PAGES, || { priv_key.hpke_open_internal(enc, ciphertext, aad, algo) }) @@ -215,7 +196,7 @@ pub fn hpke_seal( plaintext: &SecretBox, aad: &[u8], algo: &HpkeAlgorithm, -) -> Result<(Vec, Vec), Error> { +) -> Result<(Vec, Vec), Status> { clear_stack_on_return(CLEAR_STACK_PAGES, || { pub_key.hpke_seal_internal(plaintext, aad, algo) }) @@ -224,7 +205,7 @@ pub fn hpke_seal( #[cfg(test)] mod tests { use super::*; - use crate::algorithms::{AeadAlgorithm, KdfAlgorithm}; + use crate::proto::{AeadAlgorithm, KdfAlgorithm}; use bssl_crypto::hpke; #[test] @@ -257,7 +238,7 @@ mod tests { }; let result = hpke_open(&sk_r, &enc, &[], &[], &algo); - assert!(matches!(result, Err(Error::UnsupportedAlgorithm))); + assert!(matches!(result, Err(Status::UnsupportedAlgorithm))); } #[test] @@ -322,7 +303,7 @@ mod tests { } let result = hpke_open(&sk_r, &enc, &ciphertext, aad, &hpke_algo); - assert!(matches!(result, Err(Error::HpkeDecryptionError))); + assert!(matches!(result, Err(Status::DecryptionFailure))); } #[test] @@ -354,7 +335,7 @@ mod tests { let tampered_aad = b"bar"; let result = hpke_open(&sk_r, &enc, &ciphertext, tampered_aad, &hpke_algo); - assert!(matches!(result, Err(Error::HpkeDecryptionError))); + assert!(matches!(result, Err(Status::DecryptionFailure))); } #[test] @@ -400,6 +381,6 @@ mod tests { let algo = KemAlgorithm::Unspecified; let result = generate_keypair(algo); - assert!(matches!(result, Err(Error::UnsupportedAlgorithm))); + assert!(matches!(result, Err(Status::UnsupportedAlgorithm))); } } diff --git a/keymanager/km_common/src/crypto/x25519.rs b/keymanager/km_common/src/crypto/x25519.rs index 83037bc8a..9145d34c6 100644 --- a/keymanager/km_common/src/crypto/x25519.rs +++ b/keymanager/km_common/src/crypto/x25519.rs @@ -1,8 +1,8 @@ -use crate::algorithms::{AeadAlgorithm, HpkeAlgorithm, KdfAlgorithm, KemAlgorithm}; use crate::crypto::secret_box::SecretBox; #[cfg(any(test, feature = "test-utils"))] use crate::crypto::PrivateKey; -use crate::crypto::{Error, PrivateKeyOps, PublicKeyOps}; +use crate::crypto::{Status, PrivateKeyOps, PublicKeyOps}; +use crate::proto::{AeadAlgorithm, HpkeAlgorithm, KdfAlgorithm, KemAlgorithm}; use bssl_crypto::{hkdf, hpke, x25519}; /// X25519-based public key implementation. @@ -21,7 +21,7 @@ impl PublicKeyOps for X25519PublicKey { plaintext: &SecretBox, aad: &[u8], algo: &HpkeAlgorithm, - ) -> Result<(Vec, Vec), Error> { + ) -> Result<(Vec, Vec), Status> { let ( Ok(KemAlgorithm::DhkemX25519HkdfSha256), Ok(KdfAlgorithm::HkdfSha256), @@ -32,7 +32,7 @@ impl PublicKeyOps for X25519PublicKey { AeadAlgorithm::try_from(algo.aead), ) else { - return Err(Error::UnsupportedAlgorithm); + return Err(Status::UnsupportedAlgorithm); }; let params = hpke::Params::new( @@ -43,7 +43,7 @@ impl PublicKeyOps for X25519PublicKey { let (mut sender_ctx, encapsulated_key) = hpke::SenderContext::new(¶ms, self.as_ref(), b"") - .ok_or(Error::HpkeEncryptionError)?; + .ok_or(Status::EncryptionFailure)?; let ciphertext = sender_ctx.seal(plaintext.as_slice(), aad); Ok((encapsulated_key, ciphertext)) @@ -53,27 +53,27 @@ impl PublicKeyOps for X25519PublicKey { fn encap_internal( &self, ephemeral_sk: Option<&PrivateKey>, - ) -> Result<(SecretBox, Vec), Error> { + ) -> Result<(SecretBox, Vec), Status> { let (pk_e_bytes, sk_e_bytes) = match ephemeral_sk { Some(PrivateKey::X25519(sk)) => { let sk_e = x25519::PrivateKey( sk.0.as_slice() .try_into() - .map_err(|_| Error::KeyLenMismatch)?, + .map_err(|_| Status::InvalidArgument)?, ); (sk_e.to_public().to_vec(), sk.0.as_slice().to_vec()) } None => hpke::Kem::X25519HkdfSha256.generate_keypair(), }; - let sk_e = x25519::PrivateKey(sk_e_bytes.try_into().map_err(|_| Error::CryptoError)?); + let sk_e = x25519::PrivateKey(sk_e_bytes.try_into().map_err(|_| Status::CryptoError)?); let pk_r = self.0; // 1. Compute Diffie-Hellman shared secret // dh = dhExchange(skE, pkR) let shared_key = SecretBox::new( sk_e.compute_shared_key(&pk_r) - .ok_or(Error::CryptoError)? + .ok_or(Status::CryptoError)? .to_vec(), ); @@ -112,20 +112,20 @@ impl From for SecretBox { impl PrivateKeyOps for X25519PrivateKey { /// Decapsulates the shared secret from an encapsulated key. /// Follows RFC 9180 Section 4.1. DHKEM(Group, Hash). - fn decaps_internal(&self, enc: &[u8]) -> Result { + fn decaps_internal(&self, enc: &[u8]) -> Result { let priv_key = x25519::PrivateKey( self.0 .as_slice() .try_into() - .map_err(|_| Error::KeyLenMismatch)?, + .map_err(|_| Status::InvalidArgument)?, ); // 1. Compute Diffie-Hellman shared secret // dh = dhExchange(skR, pkE) let shared_key = SecretBox::new( priv_key - .compute_shared_key(enc.try_into().map_err(|_| Error::KeyLenMismatch)?) - .ok_or(Error::DecapsError)? + .compute_shared_key(enc.try_into().map_err(|_| Status::DecapsulationFailure)?) + .ok_or(Status::DecapsulationFailure)? .to_vec(), ); @@ -153,7 +153,7 @@ impl PrivateKeyOps for X25519PrivateKey { ciphertext: &[u8], aad: &[u8], algo: &HpkeAlgorithm, - ) -> Result { + ) -> Result { let ( Ok(KemAlgorithm::DhkemX25519HkdfSha256), Ok(KdfAlgorithm::HkdfSha256), @@ -164,7 +164,7 @@ impl PrivateKeyOps for X25519PrivateKey { AeadAlgorithm::try_from(algo.aead), ) else { - return Err(Error::UnsupportedAlgorithm); + return Err(Status::UnsupportedAlgorithm); }; let params = hpke::Params::new( @@ -174,12 +174,12 @@ impl PrivateKeyOps for X25519PrivateKey { ); let mut recipient_ctx = hpke::RecipientContext::new(¶ms, self.0.as_slice(), enc, b"") - .ok_or(Error::HpkeDecryptionError)?; + .ok_or(Status::DecryptionFailure)?; recipient_ctx .open(ciphertext, aad) .map(SecretBox::new) - .ok_or(Error::HpkeDecryptionError) + .ok_or(Status::DecryptionFailure) } } @@ -196,13 +196,13 @@ fn labeled_expand( info: &[u8], suite_id: &[u8], len: u16, -) -> Result { +) -> Result { let labeled_info = SecretBox::new([&len.to_be_bytes()[..], b"HPKE-v1", suite_id, label, info].concat()); let mut result = vec![0u8; len as usize]; prk.expand_into(labeled_info.as_slice(), &mut result) - .map_err(|_| Error::DecapsError)?; + .map_err(|_| Status::DecapsulationFailure)?; Ok(SecretBox::new(result)) } diff --git a/keymanager/km_common/src/key_types.rs b/keymanager/km_common/src/key_types.rs index c3e7f91d0..df0060e8e 100644 --- a/keymanager/km_common/src/key_types.rs +++ b/keymanager/km_common/src/key_types.rs @@ -1,7 +1,7 @@ -use crate::algorithms::{AeadAlgorithm, HpkeAlgorithm, KdfAlgorithm, KemAlgorithm}; use crate::crypto; use crate::crypto::{secret_box, PublicKey}; use crate::protected_mem::Vault; +use crate::proto::{AeadAlgorithm, HpkeAlgorithm, KdfAlgorithm, KemAlgorithm}; use std::collections::HashMap; use std::sync::{Arc, RwLock}; use std::time::{Duration, Instant}; @@ -134,10 +134,7 @@ impl KeyRecord { } /// Creates a new long-term Binding key. - pub fn create_binding_key( - algo: HpkeAlgorithm, - expiry: Duration, - ) -> Result { + pub fn create_binding_key(algo: HpkeAlgorithm, expiry: Duration) -> Result { Self::create_key_internal(algo, expiry, |algo, pub_key| KeySpec::Binding { algo, binding_public_key: pub_key, @@ -149,12 +146,12 @@ impl KeyRecord { algo: HpkeAlgorithm, binding_public_key: PublicKey, expiry: Duration, - ) -> Result { + ) -> Result { // Validate that the binding key is compatible with the algorithm suite. // Currently only X25519 is supported. match (&binding_public_key, KemAlgorithm::try_from(algo.kem)) { (PublicKey::X25519(_), Ok(KemAlgorithm::DhkemX25519HkdfSha256)) => (), - _ => return Err(crypto::Error::InvalidKey), + _ => return Err(crate::Status::InvalidKey), } Self::create_key_internal(algo, expiry, move |algo, pub_key| { @@ -170,7 +167,7 @@ impl KeyRecord { algo: HpkeAlgorithm, expiry: Duration, spec_builder: F, - ) -> Result + ) -> Result where F: FnOnce(HpkeAlgorithm, PublicKey) -> KeySpec, { @@ -184,19 +181,19 @@ impl KeyRecord { AeadAlgorithm::try_from(algo.aead), ) else { - return Err(crypto::Error::UnsupportedAlgorithm); + return Err(crate::Status::UnsupportedAlgorithm); }; let (pub_key, priv_key) = crypto::generate_keypair(KemAlgorithm::DhkemX25519HkdfSha256)?; let id = Uuid::new_v4(); let vault = Vault::new(secret_box::SecretBox::from(priv_key)) - .map_err(|_| crypto::Error::CryptoError)?; + .map_err(|_| crate::Status::CryptoError)?; let now = Instant::now(); let delete_after = now .checked_add(expiry) - .ok_or(crypto::Error::UnsupportedAlgorithm)?; + .ok_or(crate::Status::UnsupportedAlgorithm)?; let record = KeyRecord { meta: KeyMetadata { @@ -215,7 +212,7 @@ impl KeyRecord { #[cfg(test)] mod tests { use super::*; - use crate::algorithms::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm}; + use crate::proto::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm}; #[test] fn test_create_binding_key_success() { diff --git a/keymanager/km_common/src/keymanager.rs b/keymanager/km_common/src/keymanager.rs new file mode 100644 index 000000000..1492407ec --- /dev/null +++ b/keymanager/km_common/src/keymanager.rs @@ -0,0 +1,186 @@ +// This file is @generated by prost-build. +/// Composite HPKE Algorithm suite +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct HpkeAlgorithm { + #[prost(enumeration = "KemAlgorithm", tag = "1")] + pub kem: i32, + #[prost(enumeration = "KdfAlgorithm", tag = "2")] + pub kdf: i32, + #[prost(enumeration = "AeadAlgorithm", tag = "3")] + pub aead: i32, +} +/// Specifies parameters for a particular algorithm type. +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct AlgorithmParams { + #[prost(oneof = "algorithm_params::Params", tags = "1")] + pub params: ::core::option::Option, +} +/// Nested message and enum types in `AlgorithmParams`. +pub mod algorithm_params { + #[derive(Clone, Copy, PartialEq, ::prost::Oneof)] + pub enum Params { + /// KEM algorithm identifier (e.g., KEM_ALGORITHM_DHKEM_X25519_HKDF_SHA256). + #[prost(enumeration = "super::KemAlgorithm", tag = "1")] + KemId(i32), + } +} +/// Description of an algorithm. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlgorithmDetails { + /// The type of algorithm, e.g., "kem". + #[prost(string, tag = "1")] + pub r#type: ::prost::alloc::string::String, + /// Specific parameters associated with the algorithm type. + #[prost(message, optional, tag = "2")] + pub params: ::core::option::Option, +} +/// Represents a cryptographic algorithm supported by the service. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SupportedAlgorithm { + /// algorithm details. + #[prost(message, optional, tag = "1")] + pub algorithm: ::core::option::Option, +} +/// Key Encapsulation Mechanism (KEM) +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum KemAlgorithm { + Unspecified = 0, + DhkemX25519HkdfSha256 = 1, +} +impl KemAlgorithm { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "KEM_ALGORITHM_UNSPECIFIED", + Self::DhkemX25519HkdfSha256 => "KEM_ALGORITHM_DHKEM_X25519_HKDF_SHA256", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "KEM_ALGORITHM_UNSPECIFIED" => Some(Self::Unspecified), + "KEM_ALGORITHM_DHKEM_X25519_HKDF_SHA256" => Some(Self::DhkemX25519HkdfSha256), + _ => None, + } + } +} +/// Key Derivation Function (KDF) +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum KdfAlgorithm { + Unspecified = 0, + HkdfSha256 = 1, +} +impl KdfAlgorithm { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "KDF_ALGORITHM_UNSPECIFIED", + Self::HkdfSha256 => "KDF_ALGORITHM_HKDF_SHA256", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "KDF_ALGORITHM_UNSPECIFIED" => Some(Self::Unspecified), + "KDF_ALGORITHM_HKDF_SHA256" => Some(Self::HkdfSha256), + _ => None, + } + } +} +/// Authenticated Encryption with Associated Data (AEAD) +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum AeadAlgorithm { + Unspecified = 0, + Aes256Gcm = 1, +} +impl AeadAlgorithm { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "AEAD_ALGORITHM_UNSPECIFIED", + Self::Aes256Gcm => "AEAD_ALGORITHM_AES_256_GCM", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "AEAD_ALGORITHM_UNSPECIFIED" => Some(Self::Unspecified), + "AEAD_ALGORITHM_AES_256_GCM" => Some(Self::Aes256Gcm), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum Status { + Unspecified = 0, + Success = 1, + InternalError = 2, + InvalidArgument = 3, + NotFound = 4, + AlreadyExists = 5, + PermissionDenied = 6, + Unauthenticated = 7, + UnsupportedAlgorithm = 8, + InvalidKey = 9, + CryptoError = 10, + DecryptionFailure = 11, + EncryptionFailure = 12, + DecapsulationFailure = 13, +} +impl Status { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "STATUS_UNSPECIFIED", + Self::Success => "STATUS_SUCCESS", + Self::InternalError => "STATUS_INTERNAL_ERROR", + Self::InvalidArgument => "STATUS_INVALID_ARGUMENT", + Self::NotFound => "STATUS_NOT_FOUND", + Self::AlreadyExists => "STATUS_ALREADY_EXISTS", + Self::PermissionDenied => "STATUS_PERMISSION_DENIED", + Self::Unauthenticated => "STATUS_UNAUTHENTICATED", + Self::UnsupportedAlgorithm => "STATUS_UNSUPPORTED_ALGORITHM", + Self::InvalidKey => "STATUS_INVALID_KEY", + Self::CryptoError => "STATUS_CRYPTO_ERROR", + Self::DecryptionFailure => "STATUS_DECRYPTION_FAILURE", + Self::EncryptionFailure => "STATUS_ENCRYPTION_FAILURE", + Self::DecapsulationFailure => "STATUS_DECAPSULATION_FAILURE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "STATUS_UNSPECIFIED" => Some(Self::Unspecified), + "STATUS_SUCCESS" => Some(Self::Success), + "STATUS_INTERNAL_ERROR" => Some(Self::InternalError), + "STATUS_INVALID_ARGUMENT" => Some(Self::InvalidArgument), + "STATUS_NOT_FOUND" => Some(Self::NotFound), + "STATUS_ALREADY_EXISTS" => Some(Self::AlreadyExists), + "STATUS_PERMISSION_DENIED" => Some(Self::PermissionDenied), + "STATUS_UNAUTHENTICATED" => Some(Self::Unauthenticated), + "STATUS_UNSUPPORTED_ALGORITHM" => Some(Self::UnsupportedAlgorithm), + "STATUS_INVALID_KEY" => Some(Self::InvalidKey), + "STATUS_CRYPTO_ERROR" => Some(Self::CryptoError), + "STATUS_DECRYPTION_FAILURE" => Some(Self::DecryptionFailure), + "STATUS_ENCRYPTION_FAILURE" => Some(Self::EncryptionFailure), + "STATUS_DECAPSULATION_FAILURE" => Some(Self::DecapsulationFailure), + _ => None, + } + } +} diff --git a/keymanager/km_common/src/lib.rs b/keymanager/km_common/src/lib.rs index e07a44665..478761f2f 100644 --- a/keymanager/km_common/src/lib.rs +++ b/keymanager/km_common/src/lib.rs @@ -1,11 +1,11 @@ -pub mod keymanager { - include!(concat!(env!("OUT_DIR"), "/keymanager.rs")); -} -pub use keymanager as algorithms; +pub mod keymanager; pub use keymanager as proto; pub use proto::Status; +pub const MAX_ALGORITHM_LEN: usize = 128; +pub const MAX_PUBLIC_KEY_LEN: usize = 2048; + impl std::error::Error for Status {} impl std::fmt::Display for Status { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -18,10 +18,11 @@ pub fn ffi_call(f: F) -> Status where F: FnOnce() -> Result<(), Status>, { - std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) - .unwrap_or(Err(Status::InternalError)) - .err() - .unwrap_or(Status::Success) + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) { + Ok(Ok(())) => Status::Success, + Ok(Err(s)) => s, + Err(_) => Status::InternalError, + } } /// Helper function for FFI calls returning i32 (positive for count/success, negative for Status). @@ -29,9 +30,11 @@ pub fn ffi_call_i32(f: F) -> i32 where F: FnOnce() -> Result, { - std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) - .unwrap_or(Err(Status::InternalError)) - .unwrap_or_else(|e| -(e as i32)) + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) { + Ok(Ok(val)) => val, + Ok(Err(s)) => -(s as i32), + Err(_) => -(Status::InternalError as i32), + } } pub mod crypto; diff --git a/keymanager/workload_service/key_custody_core/cbindgen.toml b/keymanager/workload_service/key_custody_core/cbindgen.toml index 33435b557..08530f8c1 100644 --- a/keymanager/workload_service/key_custody_core/cbindgen.toml +++ b/keymanager/workload_service/key_custody_core/cbindgen.toml @@ -8,14 +8,18 @@ style = "type" documentation = false usize_is_size_t = true sys_includes = ["stdbool.h", "stddef.h", "stdint.h"] +includes = ["km_common_ffi.h"] [parse] -parse_deps = false +parse_deps = true +include = ["km_common"] clean = true [export] item_types = ["functions", "structs", "constants", "enums"] -include = ["key_manager_generate_binding_keypair", "key_manager_get_binding_key", "key_manager_destroy_binding_key", "key_manager_open", "key_manager_enumerate_binding_keys", "WsKeyInfo", "MAX_ALGORITHM_LEN", "MAX_PUBLIC_KEY_LEN"] - +include = ["key_manager_generate_binding_keypair", "key_manager_get_binding_key", "key_manager_destroy_binding_key", "key_manager_open", "key_manager_enumerate_binding_keys", "WsKeyInfo"] +exclude = ["Status", "MAX_ALGORITHM_LEN", "MAX_PUBLIC_KEY_LEN"] +[enum] +prefix_with_name = true diff --git a/keymanager/workload_service/key_custody_core/include/ws_key_custody_core.h b/keymanager/workload_service/key_custody_core/include/ws_key_custody_core.h index 477da8e87..8d022e812 100644 --- a/keymanager/workload_service/key_custody_core/include/ws_key_custody_core.h +++ b/keymanager/workload_service/key_custody_core/include/ws_key_custody_core.h @@ -6,10 +6,7 @@ #include #include #include - -#define MAX_ALGORITHM_LEN 128 - -#define MAX_PUBLIC_KEY_LEN 2048 +#include "km_common_ffi.h" typedef struct { uint8_t uuid[16]; @@ -24,35 +21,35 @@ typedef struct { extern "C" { #endif // __cplusplus -int32_t key_manager_generate_binding_keypair(const uint8_t *algo_ptr, - size_t algo_len, - uint64_t expiry_secs, - uint8_t *out_uuid, - uint8_t *out_pubkey, - size_t out_pubkey_len); - -int32_t key_manager_destroy_binding_key(const uint8_t *uuid_bytes); - -int32_t key_manager_open(const uint8_t *uuid_bytes, - const uint8_t *enc, - size_t enc_len, - const uint8_t *ciphertext, - size_t ciphertext_len, - const uint8_t *aad, - size_t aad_len, - uint8_t *out_plaintext, - size_t out_plaintext_len); +Status key_manager_generate_binding_keypair(const uint8_t *algo_ptr, + size_t algo_len, + uint64_t expiry_secs, + uint8_t *out_uuid, + uint8_t *out_pubkey, + size_t out_pubkey_len); + +Status key_manager_destroy_binding_key(const uint8_t *uuid_bytes); + +Status key_manager_open(const uint8_t *uuid_bytes, + const uint8_t *enc, + size_t enc_len, + const uint8_t *ciphertext, + size_t ciphertext_len, + const uint8_t *aad, + size_t aad_len, + uint8_t *out_plaintext, + size_t out_plaintext_len); int32_t key_manager_enumerate_binding_keys(WsKeyInfo *out_entries, size_t max_entries, size_t offset, bool *out_has_more); -int32_t key_manager_get_binding_key(const uint8_t *uuid_bytes, - uint8_t *out_pubkey, - size_t out_pubkey_len, - uint8_t *out_algo, - size_t *out_algo_len); +Status key_manager_get_binding_key(const uint8_t *uuid_bytes, + uint8_t *out_pubkey, + size_t out_pubkey_len, + uint8_t *out_algo, + size_t *out_algo_len); #ifdef __cplusplus } // extern "C" diff --git a/keymanager/workload_service/key_custody_core/integration_test.go b/keymanager/workload_service/key_custody_core/integration_test.go index a3d94007f..a10399a1a 100644 --- a/keymanager/workload_service/key_custody_core/integration_test.go +++ b/keymanager/workload_service/key_custody_core/integration_test.go @@ -4,6 +4,7 @@ package wskcc import ( "bytes" + "strings" "testing" "github.com/google/uuid" @@ -77,4 +78,9 @@ func TestIntegrationGetBindingKeyNotFound(t *testing.T) { if err == nil { t.Fatal("expected error for non-existent UUID") } + + expectedErrMsg := "FFI status: STATUS_NOT_FOUND" + if !strings.Contains(err.Error(), expectedErrMsg) { + t.Fatalf("expected error containing %q, got: %v", expectedErrMsg, err) + } } diff --git a/keymanager/workload_service/key_custody_core/src/lib.rs b/keymanager/workload_service/key_custody_core/src/lib.rs index c895ea0ba..8d1b8c403 100644 --- a/keymanager/workload_service/key_custody_core/src/lib.rs +++ b/keymanager/workload_service/key_custody_core/src/lib.rs @@ -1,7 +1,8 @@ -use km_common::algorithms::HpkeAlgorithm; use km_common::crypto::PublicKey; use km_common::crypto::secret_box::SecretBox; use km_common::key_types::{KeyRecord, KeyRegistry, KeySpec}; +use km_common::proto::{HpkeAlgorithm, Status}; +use km_common::{MAX_ALGORITHM_LEN, MAX_PUBLIC_KEY_LEN}; use prost::Message; use std::slice; use std::sync::Arc; @@ -20,23 +21,18 @@ static KEY_REGISTRY: LazyLock = LazyLock::new(|| { fn generate_binding_keypair_internal( algo: HpkeAlgorithm, expiry_secs: u64, -) -> Result<(uuid::Uuid, PublicKey), i32> { - let result = KeyRecord::create_binding_key(algo, Duration::from_secs(expiry_secs)); - - match result { - Ok(record) => { - let id = record.meta.id; - let pubkey = match &record.meta.spec { - KeySpec::Binding { - binding_public_key, .. - } => binding_public_key.clone(), - _ => return Err(-1), - }; - KEY_REGISTRY.add_key(record); - Ok((id, pubkey)) - } - Err(_) => Err(-1), - } +) -> Result<(uuid::Uuid, PublicKey), Status> { + let record = KeyRecord::create_binding_key(algo, Duration::from_secs(expiry_secs))?; + + let id = record.meta.id; + let pubkey = match &record.meta.spec { + KeySpec::Binding { + binding_public_key, .. + } => binding_public_key.clone(), + _ => return Err(Status::InternalError), + }; + KEY_REGISTRY.add_key(record); + Ok((id, pubkey)) } /// Generates a new binding HPKE keypair. @@ -59,9 +55,9 @@ fn generate_binding_keypair_internal( /// * `out_pubkey_len` is either null or points to a valid `usize`. /// /// ## Returns -/// * `0` on success. -/// * `-1` if an error occurred during key generation. -/// * `-2` if the `out_pubkey` buffer size does not match the key size. +/// * `Status::Success` on success. +/// * `Status::InvalidArgument` if an error occurred during key generation. +/// * Other `Status` values on failure. #[unsafe(no_mangle)] pub unsafe extern "C" fn key_manager_generate_binding_keypair( algo_ptr: *const u8, @@ -70,11 +66,11 @@ pub unsafe extern "C" fn key_manager_generate_binding_keypair( out_uuid: *mut u8, out_pubkey: *mut u8, out_pubkey_len: usize, -) -> i32 { - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { +) -> Status { + km_common::ffi_call(|| { // Safety Invariant Checks if out_pubkey.is_null() || out_uuid.is_null() || algo_ptr.is_null() || algo_len == 0 { - return -1; + return Err(Status::InvalidArgument); } // Convert to Safe Types @@ -82,25 +78,17 @@ pub unsafe extern "C" fn key_manager_generate_binding_keypair( let out_uuid = unsafe { slice::from_raw_parts_mut(out_uuid, 16) }; let out_pubkey = unsafe { slice::from_raw_parts_mut(out_pubkey, out_pubkey_len) }; - let algo = match HpkeAlgorithm::decode(algo_slice) { - Ok(a) => a, - Err(_) => return -1, - }; + let algo = HpkeAlgorithm::decode(algo_slice).map_err(|_| Status::InvalidArgument)?; // Call Safe Internal Function - match generate_binding_keypair_internal(algo, expiry_secs) { - Ok((id, pubkey)) => { - if out_pubkey_len != pubkey.as_bytes().len() { - return -2; - } - out_uuid.copy_from_slice(id.as_bytes()); - out_pubkey.copy_from_slice(pubkey.as_bytes()); - 0 // Success - } - Err(e) => e, + let (id, pubkey) = generate_binding_keypair_internal(algo, expiry_secs)?; + if out_pubkey_len != pubkey.as_bytes().len() { + return Err(Status::InvalidArgument); } - })) - .unwrap_or(-1) + out_uuid.copy_from_slice(id.as_bytes()); + out_pubkey.copy_from_slice(pubkey.as_bytes()); + Ok(()) + }) } /// Destroys the binding key associated with the given UUID. @@ -113,40 +101,39 @@ pub unsafe extern "C" fn key_manager_generate_binding_keypair( /// The caller must ensure that `uuid_bytes` points to a valid 16-byte buffer. /// /// ## Returns -/// * `0` on success. -/// * `-1` if the UUID pointer is null or the key was not found. +/// * `Status::Success` on success. +/// * `Status::InvalidArgument` if the UUID pointer is null. +/// * `Status::NotFound` if the key was not found. #[unsafe(no_mangle)] -pub unsafe extern "C" fn key_manager_destroy_binding_key(uuid_bytes: *const u8) -> i32 { - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { +pub unsafe extern "C" fn key_manager_destroy_binding_key(uuid_bytes: *const u8) -> Status { + km_common::ffi_call(|| { if uuid_bytes.is_null() { - return -1; + return Err(Status::InvalidArgument); } let bytes = unsafe { slice::from_raw_parts(uuid_bytes, 16) }; - let uuid = Uuid::from_bytes(bytes.try_into().expect("invalid UUID bytes")); + let uuid = Uuid::from_bytes(bytes.try_into().map_err(|_| Status::InvalidArgument)?); - match KEY_REGISTRY.remove_key(&uuid) { - Some(_) => 0, // Success - None => -1, // Not found - } - })) - .unwrap_or(-1) + KEY_REGISTRY.remove_key(&uuid).ok_or(Status::NotFound)?; + Ok(()) + }) } /// Internal function to decrypt a ciphertext using a stored binding key. -fn open_internal(uuid: Uuid, enc: &[u8], ciphertext: &[u8], aad: &[u8]) -> Result { - let record = KEY_REGISTRY.get_key(&uuid).ok_or(-1)?; +fn open_internal( + uuid: Uuid, + enc: &[u8], + ciphertext: &[u8], + aad: &[u8], +) -> Result { + let record = KEY_REGISTRY.get_key(&uuid).ok_or(Status::NotFound)?; let algo = match &record.meta.spec { KeySpec::Binding { algo, .. } => algo, - _ => return Err(-1), + _ => return Err(Status::InternalError), }; let priv_key = record.get_private_key(); - - match km_common::crypto::hpke_open(&priv_key, enc, ciphertext, aad, algo) { - Ok(pt) => Ok(pt), - Err(_) => Err(-3), - } + km_common::crypto::hpke_open(&priv_key, enc, ciphertext, aad, algo) } /// Decrypts a ciphertext using the binding key associated with the given UUID. @@ -172,10 +159,10 @@ fn open_internal(uuid: Uuid, enc: &[u8], ciphertext: &[u8], aad: &[u8]) -> Resul /// * `out_plaintext_len` points to a valid `usize`. /// /// ## Returns -/// * `0` on success. -/// * `-1` if arguments are invalid (e.g., key not found, null pointers). -/// * `-2` if the `out_plaintext` buffer is too small. -/// * `-3` if decryption failed. +/// * `Status::Success` on success. +/// * `Status::InvalidArgument` if arguments are invalid or output buffer is too small. +/// * `Status::NotFound` if the key was not found. +/// * `Status::DecryptionFailure` if decryption failed. #[unsafe(no_mangle)] pub unsafe extern "C" fn key_manager_open( uuid_bytes: *const u8, @@ -187,15 +174,15 @@ pub unsafe extern "C" fn key_manager_open( aad_len: usize, out_plaintext: *mut u8, out_plaintext_len: usize, -) -> i32 { - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { +) -> Status { + km_common::ffi_call(|| { if uuid_bytes.is_null() || enc.is_null() || ciphertext.is_null() || out_plaintext.is_null() || out_plaintext_len == 0 { - return -1; + return Err(Status::InvalidArgument); } let uuid_slice = unsafe { std::slice::from_raw_parts(uuid_bytes, 16) }; @@ -209,29 +196,18 @@ pub unsafe extern "C" fn key_manager_open( let out_plaintext_slice = unsafe { std::slice::from_raw_parts_mut(out_plaintext, out_plaintext_len) }; - let uuid = match Uuid::from_slice(uuid_slice) { - Ok(u) => u, - Err(_) => return -1, - }; + let uuid = Uuid::from_slice(uuid_slice).map_err(|_| Status::InvalidArgument)?; // Call Safe Internal Function - match open_internal(uuid, enc_slice, ct_slice, aad_slice) { - Ok(pt) => { - if out_plaintext_len != pt.as_slice().len() { - return -2; - } - out_plaintext_slice.copy_from_slice(pt.as_slice()); - 0 // Success - } - Err(e) => e, + let pt = open_internal(uuid, enc_slice, ct_slice, aad_slice)?; + if out_plaintext_len != pt.as_slice().len() { + return Err(Status::InvalidArgument); } - })) - .unwrap_or(-1) + out_plaintext_slice.copy_from_slice(pt.as_slice()); + Ok(()) + }) } -pub const MAX_ALGORITHM_LEN: usize = 128; -pub const MAX_PUBLIC_KEY_LEN: usize = 2048; - #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct WsKeyInfo { @@ -259,7 +235,7 @@ impl Default for WsKeyInfo { fn enumerate_binding_keys_internal( entries: &mut [WsKeyInfo], offset: usize, -) -> Result<(usize, bool), i32> { +) -> Result<(usize, bool), Status> { let (metas, total_count) = KEY_REGISTRY.list_all_keys(offset, entries.len()); let count = metas.len(); let has_more = offset + count < total_count; @@ -270,7 +246,7 @@ fn enumerate_binding_keys_internal( binding_public_key: pub_key, } = &meta.spec else { - return Err(-1); + return Err(Status::InternalError); }; let algo_bytes = algo.encode_to_vec(); @@ -282,7 +258,7 @@ fn enumerate_binding_keys_internal( algo_bytes.len(), pub_key.as_bytes().len() ); - return Err(-2); // Buffer Limit Exceeded + return Err(Status::InternalError); // Buffer Limit Exceeded } let now = std::time::Instant::now(); @@ -311,35 +287,30 @@ pub unsafe extern "C" fn key_manager_enumerate_binding_keys( offset: usize, out_has_more: Option<&mut bool>, ) -> i32 { - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + km_common::ffi_call_i32(|| { if out_entries.is_null() { - return -1; + return Err(Status::InvalidArgument); } let entries = unsafe { slice::from_raw_parts_mut(out_entries, max_entries) }; - match enumerate_binding_keys_internal(entries, offset) { - Ok((count, has_more)) => { - if let Some(has_more_ref) = out_has_more { - *has_more_ref = has_more; - } - count as i32 - } - Err(e) => e, + let (count, has_more) = enumerate_binding_keys_internal(entries, offset)?; + if let Some(has_more_ref) = out_has_more { + *has_more_ref = has_more; } - })) - .unwrap_or(-1) + Ok(count as i32) + }) } /// Internal function to retrieve a binding key's public key and algorithm. -fn get_binding_key_internal(uuid: Uuid) -> Result<(HpkeAlgorithm, PublicKey), i32> { - let record = KEY_REGISTRY.get_key(&uuid).ok_or(-1)?; +fn get_binding_key_internal(uuid: Uuid) -> Result<(HpkeAlgorithm, PublicKey), Status> { + let record = KEY_REGISTRY.get_key(&uuid).ok_or(Status::NotFound)?; match &record.meta.spec { KeySpec::Binding { algo, binding_public_key, } => Ok((algo.clone(), binding_public_key.clone())), - _ => Err(-1), + _ => Err(Status::InternalError), } } @@ -356,9 +327,9 @@ fn get_binding_key_internal(uuid: Uuid) -> Result<(HpkeAlgorithm, PublicKey), i3 /// This function is unsafe because it dereferences raw pointers. /// /// ## Returns -/// * `0` on success. -/// * `-1` if arguments are invalid or key is not found. -/// * `-2` if either output buffer is too small. +/// * `Status::Success` on success. +/// * `Status::InvalidArgument` if arguments are invalid. +/// * `Status::NotFound` if key is not found. #[unsafe(no_mangle)] pub unsafe extern "C" fn key_manager_get_binding_key( uuid_bytes: *const u8, @@ -366,15 +337,15 @@ pub unsafe extern "C" fn key_manager_get_binding_key( out_pubkey_len: usize, out_algo: *mut u8, out_algo_len: *mut usize, -) -> i32 { - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { +) -> Status { + km_common::ffi_call(|| { if uuid_bytes.is_null() || out_pubkey.is_null() || out_pubkey_len == 0 || out_algo.is_null() || out_algo_len.is_null() { - return -1; + return Err(Status::InvalidArgument); } // Convert to Safe Types @@ -382,37 +353,28 @@ pub unsafe extern "C" fn key_manager_get_binding_key( let out_pubkey_slice = unsafe { std::slice::from_raw_parts_mut(out_pubkey, out_pubkey_len) }; let out_algo_len_ref = unsafe { &mut *out_algo_len }; - let out_algo_slice = unsafe { std::slice::from_raw_parts_mut(out_algo, *out_algo_len) }; + let out_algo_slice = unsafe { std::slice::from_raw_parts_mut(out_algo, *out_algo_len_ref) }; - let uuid = match Uuid::from_slice(uuid_slice) { - Ok(u) => u, - Err(_) => return -1, - }; + let uuid = Uuid::from_slice(uuid_slice).map_err(|_| Status::InvalidArgument)?; // Call Safe Internal Function - match get_binding_key_internal(uuid) { - Ok((algo, pubkey)) => { - let algo_bytes = algo.encode_to_vec(); - if out_pubkey_slice.len() != pubkey.as_bytes().len() - || *out_algo_len_ref < algo_bytes.len() - { - return -2; - } - out_pubkey_slice.copy_from_slice(pubkey.as_bytes()); - out_algo_slice[..algo_bytes.len()].copy_from_slice(&algo_bytes); - *out_algo_len_ref = algo_bytes.len(); - 0 // Success - } - Err(e) => e, + let (algo, pubkey) = get_binding_key_internal(uuid)?; + let algo_bytes = algo.encode_to_vec(); + if out_pubkey_slice.len() != pubkey.as_bytes().len() || *out_algo_len_ref < algo_bytes.len() + { + return Err(Status::InvalidArgument); } - })) - .unwrap_or(-1) + out_pubkey_slice.copy_from_slice(pubkey.as_bytes()); + out_algo_slice[..algo_bytes.len()].copy_from_slice(&algo_bytes); + *out_algo_len_ref = algo_bytes.len(); + Ok(()) + }) } #[cfg(test)] mod tests { use super::*; - use km_common::algorithms::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm}; + use km_common::proto::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm}; use prost::Message; struct KeyCleanup(Uuid); @@ -463,7 +425,7 @@ mod tests { ) }; - assert_eq!(result, 0); + assert_eq!(result, Status::Success.into()); let _cleanup = KeyCleanup(Uuid::from_bytes(uuid_bytes)); assert_ne!(uuid_bytes, [0u8; 16]); @@ -490,7 +452,7 @@ mod tests { ) }; - assert_eq!(result, -1); + assert_eq!(result, Status::InvalidArgument); assert_eq!(uuid_bytes, [0u8; 16]); // Should remain untouched/zero } @@ -503,7 +465,6 @@ mod tests { }; let algo_bytes = algo.encode_to_vec(); - // Pass null pointers, should succeed (return 0) but not crash let result = unsafe { key_manager_generate_binding_keypair( algo_bytes.as_ptr(), @@ -515,7 +476,7 @@ mod tests { ) }; - assert_eq!(result, -1); + assert_eq!(result, Status::InvalidArgument); } #[test] @@ -541,7 +502,7 @@ mod tests { ) }; - assert_eq!(result, -2); + assert_eq!(result, Status::InvalidArgument); assert_eq!(uuid_bytes, [0u8; 16]); // Should remain untouched/zero assert_eq!(&pubkey_bytes[..32], &[0u8; 32]); // Should remain untouched/zero } @@ -567,28 +528,28 @@ mod tests { pubkey_len, ) }; - assert_eq!(res, 0); + assert_eq!(res, Status::Success); let _cleanup = KeyCleanup(Uuid::from_bytes(uuid_bytes)); let result = unsafe { key_manager_destroy_binding_key(uuid_bytes.as_ptr()) }; - assert_eq!(result, 0); + assert_eq!(result, Status::Success.into()); // Second destroy should fail let result = unsafe { key_manager_destroy_binding_key(uuid_bytes.as_ptr()) }; - assert_eq!(result, -1); + assert_eq!(result, Status::NotFound.into()); } #[test] fn test_destroy_binding_key_not_found() { let uuid_bytes = [0u8; 16]; let result = unsafe { key_manager_destroy_binding_key(uuid_bytes.as_ptr()) }; - assert_eq!(result, -1); + assert_eq!(result, Status::NotFound.into()); } #[test] fn test_destroy_binding_key_null_ptr() { let result = unsafe { key_manager_destroy_binding_key(std::ptr::null()) }; - assert_eq!(result, -1); + assert_eq!(result, Status::InvalidArgument); } #[test] @@ -640,7 +601,7 @@ mod tests { ) }; - assert_eq!(result, 0); + assert_eq!(result, Status::Success.into()); assert_eq!(&out_pt, PT); } @@ -662,7 +623,7 @@ mod tests { out_pt_len, ) }; - assert_eq!(result, -1); + assert_eq!(result, Status::NotFound.into()); } #[test] @@ -686,7 +647,11 @@ mod tests { pubkey_len, ) }; - assert_eq!(res, 0, "Setup failed: key generation returned error"); + assert_eq!( + res, + Status::Success, + "Setup failed: key generation returned error" + ); let _cleanup = KeyCleanup(Uuid::from_bytes(uuid_bytes)); let pt = b"secret message"; @@ -710,7 +675,7 @@ mod tests { ) }; - assert_eq!(result, -2); + assert_eq!(result, Status::InvalidArgument); assert_eq!(out_pt, [0u8; 5]); // Should remain untouched/zero } @@ -737,7 +702,7 @@ mod tests { pubkey_len, ) }; - assert_eq!(result, 0); + assert_eq!(result, Status::Success.into()); let _cleanup = KeyCleanup(Uuid::from_bytes(uuid_bytes)); let mut out_entries = [WsKeyInfo::default(); 32]; @@ -787,7 +752,7 @@ mod tests { pubkey_len, ) }; - assert_eq!(res, 0); + assert_eq!(res, Status::Success); let _cleanup = KeyCleanup(Uuid::from_bytes(uuid_bytes)); // Now, retrieve it. @@ -804,7 +769,7 @@ mod tests { ) }; - assert_eq!(result, 0); + assert_eq!(result, Status::Success.into()); assert_eq!(generated_pubkey_bytes, retrieved_pubkey_bytes); assert_eq!(algo_bytes, &retrieved_algo_bytes[..retrieved_algo_len]); } @@ -825,6 +790,6 @@ mod tests { &mut retrieved_algo_len, ) }; - assert_eq!(result, -1); + assert_eq!(result, Status::NotFound.into()); } } diff --git a/keymanager/workload_service/key_custody_core/ws_key_custody_core_cgo.go b/keymanager/workload_service/key_custody_core/ws_key_custody_core_cgo.go index c6a1c1e23..8807b17a2 100644 --- a/keymanager/workload_service/key_custody_core/ws_key_custody_core_cgo.go +++ b/keymanager/workload_service/key_custody_core/ws_key_custody_core_cgo.go @@ -6,6 +6,7 @@ package wskcc /* +#cgo CFLAGS: -I${SRCDIR}/../../km_common/include #cgo LDFLAGS: -L${SRCDIR}/../../target/release -L${SRCDIR}/../../target/debug -lws_key_custody_core #cgo LDFLAGS: -lcrypto -lssl #cgo LDFLAGS: -lpthread -ldl -lm -lstdc++ @@ -48,8 +49,8 @@ func GenerateBindingKeypair(algo *keymanager.HpkeAlgorithm, lifespanSecs uint64) (*C.uint8_t)(unsafe.Pointer(&uuidBytes[0])), (*C.uint8_t)(unsafe.Pointer(&pubkeyBuf[0])), pubkeyLen, - ); rc != 0 { - return uuid.Nil, nil, fmt.Errorf("key_manager_generate_binding_keypair failed with code %d", rc) + ); keymanager.Status(rc) != keymanager.Status_STATUS_SUCCESS { + return uuid.Nil, nil, keymanager.Status(rc).ToStatus() } id, err := uuid.FromBytes(uuidBytes[:]) @@ -68,10 +69,7 @@ func DestroyBindingKey(bindingUUID uuid.UUID) error { rc := C.key_manager_destroy_binding_key( (*C.uint8_t)(unsafe.Pointer(&uuidBytes[0])), ) - if rc != 0 { - return fmt.Errorf("key_manager_destroy_binding_key failed with code %d", rc) - } - return nil + return keymanager.Status(rc).ToStatus() } // Open decrypts a sealed ciphertext using the binding key identified by @@ -110,8 +108,8 @@ func Open(bindingUUID uuid.UUID, enc, ciphertext, aad []byte) ([]byte, error) { aadLen, (*C.uint8_t)(unsafe.Pointer(&outPT[0])), outPTLen, - ); rc != 0 { - return nil, fmt.Errorf("key_manager_open failed with code %d", rc) + ); keymanager.Status(rc) != keymanager.Status_STATUS_SUCCESS { + return nil, keymanager.Status(rc).ToStatus() } plaintext := make([]byte, outPTLen) @@ -129,15 +127,14 @@ func GetBindingKey(id uuid.UUID) ([]byte, *keymanager.HpkeAlgorithm, error) { var algoBuf [C.MAX_ALGORITHM_LEN]byte algoLenC := C.size_t(len(algoBuf)) - rc := C.key_manager_get_binding_key( + if rc := C.key_manager_get_binding_key( (*C.uint8_t)(unsafe.Pointer(&uuidBytes[0])), (*C.uint8_t)(unsafe.Pointer(&pubkeyBuf[0])), pubkeyLen, (*C.uint8_t)(unsafe.Pointer(&algoBuf[0])), &algoLenC, - ) - if rc != 0 { - return nil, nil, fmt.Errorf("key_manager_get_binding_key failed with code %d", rc) + ); keymanager.Status(rc) != keymanager.Status_STATUS_SUCCESS { + return nil, nil, keymanager.Status(rc).ToStatus() } pubkey := make([]byte, pubkeyLen) diff --git a/keymanager/workload_service/server.go b/keymanager/workload_service/server.go index dff9ea5ae..d574a19dd 100644 --- a/keymanager/workload_service/server.go +++ b/keymanager/workload_service/server.go @@ -241,6 +241,29 @@ func (s *Server) LookupBindingUUID(kemUUID uuid.UUID) (uuid.UUID, bool) { return id, ok } +func httpStatusFromError(err error) int { + if err == nil { + return http.StatusOK + } + + switch { + case errors.Is(err, keymanager.Status_STATUS_NOT_FOUND): + return http.StatusNotFound + case errors.Is(err, keymanager.Status_STATUS_INVALID_ARGUMENT), + errors.Is(err, keymanager.Status_STATUS_UNSUPPORTED_ALGORITHM), + errors.Is(err, keymanager.Status_STATUS_INVALID_KEY): + return http.StatusBadRequest + case errors.Is(err, keymanager.Status_STATUS_PERMISSION_DENIED): + return http.StatusForbidden + case errors.Is(err, keymanager.Status_STATUS_UNAUTHENTICATED): + return http.StatusUnauthorized + case errors.Is(err, keymanager.Status_STATUS_ALREADY_EXISTS): + return http.StatusConflict + default: + return http.StatusInternalServerError + } +} + func decapsAADContext(kemUUID uuid.UUID, algorithm keymanager.KemAlgorithm) []byte { // Bind the KPS->WSD transport ciphertext to this decapsulation context. // Note: The AAD context string retains `decaps` as it is part of the internal binding protocol @@ -284,14 +307,14 @@ func (s *Server) handleDecaps(w http.ResponseWriter, r *http.Request) { // Decapsulate and reseal via KPS. sealEnc, sealedCT, err := s.keyProtectionService.DecapAndSeal(kemUUID, encapsulatedKey, aad) if err != nil { - writeError(w, fmt.Sprintf("failed to decap and seal: %v", err), http.StatusInternalServerError) + writeError(w, fmt.Sprintf("failed to decap and seal: %v", err), httpStatusFromError(err)) return } // Open the sealed secret using the binding key via WSD KCC. plaintext, err := s.workloadService.Open(bindingUUID, sealEnc, sealedCT, aad) if err != nil { - writeError(w, fmt.Sprintf("failed to open sealed secret: %v", err), http.StatusInternalServerError) + writeError(w, fmt.Sprintf("failed to open sealed secret: %v", err), httpStatusFromError(err)) return } @@ -338,14 +361,14 @@ func (s *Server) generateKEMKey(w http.ResponseWriter, req *api.GenerateKeyReque // Generate binding keypair via WSD KCC FFI. bindingUUID, bindingPubKey, err := s.workloadService.GenerateBindingKeypair(algo, req.Lifespan) if err != nil { - writeError(w, fmt.Sprintf("failed to generate binding keypair: %v", err), http.StatusInternalServerError) + writeError(w, fmt.Sprintf("failed to generate binding keypair: %v", err), httpStatusFromError(err)) return } // Generate KEM keypair via KPS KOL, passing the binding public key. kemUUID, kemPubKey, err := s.keyProtectionService.GenerateKEMKeypair(algo, bindingPubKey, req.Lifespan) if err != nil { - writeError(w, fmt.Sprintf("failed to generate KEM keypair: %v", err), http.StatusInternalServerError) + writeError(w, fmt.Sprintf("failed to generate KEM keypair: %v", err), httpStatusFromError(err)) return } @@ -400,7 +423,7 @@ func (s *Server) handleGetCapabilities(w http.ResponseWriter, _ *http.Request) { func (s *Server) handleEnumerateKeys(w http.ResponseWriter, _ *http.Request) { keys, _, err := s.keyProtectionService.EnumerateKEMKeys(100, 0) if err != nil { - writeError(w, fmt.Sprintf("failed to enumerate keys: %v", err), http.StatusInternalServerError) + writeError(w, fmt.Sprintf("failed to enumerate keys: %v", err), httpStatusFromError(err)) return } @@ -517,7 +540,7 @@ func (s *Server) handleDestroy(w http.ResponseWriter, r *http.Request) { s.mu.Unlock() if err := errors.Join(errKps, errWs); err != nil { - writeError(w, fmt.Sprintf("failed to destroy keys: %v", err), http.StatusInternalServerError) + writeError(w, fmt.Sprintf("failed to destroy keys: %v", err), httpStatusFromError(err)) return }