Skip to main content

KeyCustody

Trait KeyCustody 

Source
pub trait KeyCustody: Send + Sync {
    // Required methods
    fn generate_keypair(
        &self,
        key_type: KeyType,
    ) -> impl Future<Output = Result<KeyHandle, PlatformError>> + Send;
    fn sign(
        &self,
        key: &KeyHandle,
        data: &[u8],
    ) -> impl Future<Output = Result<Signature, PlatformError>> + Send;
    fn public_key(
        &self,
        key: &KeyHandle,
    ) -> impl Future<Output = Result<PublicKey, PlatformError>> + Send;
    fn destroy_key(
        &self,
        key: &KeyHandle,
    ) -> impl Future<Output = Result<(), PlatformError>> + Send;
    fn dh_agree(
        &self,
        key: &KeyHandle,
        peer_public: &[u8; 32],
    ) -> impl Future<Output = Result<SharedSecret, PlatformError>> + Send;
    fn derive_pseudonym(
        &self,
        key: &KeyHandle,
        context_id: &[u8],
    ) -> impl Future<Output = Result<PseudonymKeypair, PlatformError>> + Send;
    fn derive_rotatable_pseudonym(
        &self,
        key: &KeyHandle,
        context_id: &[u8],
        pseudonym_epoch: u64,
    ) -> impl Future<Output = Result<PseudonymKeypair, PlatformError>> + Send;
    fn custody_type(&self, key: &KeyHandle) -> CustodyType;
}
Expand description

Cryptographic key management trait.

Abstracts key generation, signing, key agreement, and pseudonym derivation behind a uniform interface. Production implementations delegate to hardware security modules (Secure Enclave on iOS, Android Keystore on Android). The testing implementation (InMemoryKeyCustody) stores keys in a HashMap.

All methods that perform I/O or hardware interaction are async. The custody_type method is synchronous because it only inspects local state.

See ADR-006 for the full design rationale.

Required Methods§

Source

fn generate_keypair( &self, key_type: KeyType, ) -> impl Future<Output = Result<KeyHandle, PlatformError>> + Send

Generate a new keypair of the specified type.

Ed25519 keys may be hardware-backed (Secure Enclave, Keystore). X25519 wrapping keys are always software-managed but routed through KeyCustody for API consistency.

Returns an opaque KeyHandle that references the generated key.

Source

fn sign( &self, key: &KeyHandle, data: &[u8], ) -> impl Future<Output = Result<Signature, PlatformError>> + Send

Sign data with an Ed25519 key.

§Errors

Returns PlatformError::KeyNotFound if the handle is invalid. Returns PlatformError::WrongKeyType if the handle refers to an X25519 key.

Source

fn public_key( &self, key: &KeyHandle, ) -> impl Future<Output = Result<PublicKey, PlatformError>> + Send

Return the public key for a handle.

Works for both Ed25519 and X25519 key handles.

§Errors

Returns PlatformError::KeyNotFound if the handle is invalid.

Source

fn destroy_key( &self, key: &KeyHandle, ) -> impl Future<Output = Result<(), PlatformError>> + Send

Destroy key material associated with a handle.

After this call, all subsequent operations with the same handle will return PlatformError::KeyNotFound.

§Errors

Returns PlatformError::KeyNotFound if the handle is already invalid.

Source

fn dh_agree( &self, key: &KeyHandle, peer_public: &[u8; 32], ) -> impl Future<Output = Result<SharedSecret, PlatformError>> + Send

Perform X25519 Diffie-Hellman key agreement.

Returns the 32-byte shared secret. The private key never leaves the custody boundary — the scalar multiplication happens inside the adapter.

§Errors

Returns PlatformError::KeyNotFound if the handle is invalid. Returns PlatformError::WrongKeyType if the handle refers to an Ed25519 key.

Source

fn derive_pseudonym( &self, key: &KeyHandle, context_id: &[u8], ) -> impl Future<Output = Result<PseudonymKeypair, PlatformError>> + Send

Derive a deterministic, context-scoped pseudonym keypair (v1, non-rotatable).

Algorithm (all implementations MUST produce identical output):

  1. seed = HMAC-SHA256(identity_key_material, context_id || "scp-pseudonym")
  2. pseudonym_keypair = Ed25519_keygen(seed[0..32])

For hardware-backed keys: the HMAC is computed inside the HSM using an associated symmetric key derived during generate_keypair. For software keys: the HMAC uses the raw Ed25519 public key bytes (ADR-027 amendment).

The returned PseudonymKeypair is always software-managed (derived output).

For contexts that support pseudonym rotation (BLACK-001 mitigation), use derive_rotatable_pseudonym instead.

§Errors

Returns PlatformError::KeyNotFound if the handle is invalid. Returns PlatformError::WrongKeyType if the handle refers to an X25519 key.

Source

fn derive_rotatable_pseudonym( &self, key: &KeyHandle, context_id: &[u8], pseudonym_epoch: u64, ) -> impl Future<Output = Result<PseudonymKeypair, PlatformError>> + Send

Derive a rotatable, epoch-scoped pseudonym keypair (v2).

Mitigates relay-side pseudonym correlation (BLACK-001) by including a rotation epoch in the HMAC derivation, producing a different pseudonym for each epoch within the same context.

Algorithm (all implementations MUST produce identical output):

  1. seed = HMAC-SHA256(identity_key_material, context_id || epoch_BE || "scp-pseudonym-v2")
  2. pseudonym_keypair = Ed25519_keygen(seed[0..32])

where epoch_BE is the pseudonym_epoch as an 8-byte big-endian u64.

The domain separator "scp-pseudonym-v2" is intentionally different from the v1 separator "scp-pseudonym" so that epoch 0 in v2 produces a different pseudonym than the v1 derivation. This prevents accidental domain confusion.

§Errors

Returns PlatformError::KeyNotFound if the handle is invalid. Returns PlatformError::WrongKeyType if the handle refers to an X25519 key.

Source

fn custody_type(&self, key: &KeyHandle) -> CustodyType

Returns the custody type for a given key handle.

This is a synchronous query against local state — no I/O is required.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§