Cryptographic Interface

libedhoc computes no cryptography itself: every primitive is reached through a single user-supplied callback interface, the operations interface:

  • Ephemeral key exchange — a KEM: generate_key_pair / encapsulate / decapsulate.

  • Static Diffie-Hellmankey_agreement (NIKE suites, methods 1/2/3).

  • AEADaead_encrypt / aead_decrypt.

  • Hash — multipart hash_init / hash_update / hash_finish.

  • Key derivationHKDF extract / expand (handle and raw forms).

  • Signaturesign / verify.

Keys never cross the boundary as raw bytes: they live in the backend key store and are passed by opaque handle only, and each derived handle carries the key-usage policy it will serve.

Ready-made, production-ready bindings for every supported suite live under library/cipher_suites/; see Cipher Suites.

Header file: include/edhoc/crypto.h

Key usage

group EDHOC interface for cryptographic key usage

Enums

enum edhoc_key_usage

Policy of a key handle produced by edhoc_crypto::expand.

Values:

enumerator EDHOC_KEY_USAGE_KDF

Key used as input to key derivation (EDHOC_Extract / EDHOC_Expand).

enumerator EDHOC_KEY_USAGE_AEAD

Key used for AEAD encryption and decryption.

Operations

group EDHOC interface for cryptographic operations
struct edhoc_crypto
#include <crypto.h>

Cryptographic operations vtable (handle-based).

  - Every entry is mandatory; \ref edhoc_bind_crypto rejects a vtable
    with a missing one. A suite that cannot perform an operation still
    supplies the entry and fails it with #EDHOC_ERROR_NOT_SUPPORTED.
  - Every long-lived secret is an opaque handle into the backend key
    store (a software PSA slot, the TrustZone secure world or a secure
    element); it is never serialized into \ref edhoc_context or onto
    the stack.
  - Peer public keys enter as raw bytes; one-shot public outputs
    (keystreams, IVs, MACs) leave as raw bytes.
  - The ephemeral key exchange is modelled as a KEM; a classical
    Diffie-Hellman suite implements it as a thin NIKE-as-KEM shim
    (\c G_X carries the encapsulation key, \c G_Y the ciphertext), so
    ML-KEM drops in behind the same interface with the wire unchanged.

Public Members

int (*destroy_key)(void *user_context, void *key_id)

Destroy a key handle and free its key-store slot.

  Destroying a zeroed / no-key handle is a successful no-op, so
  \ref edhoc_context_deinit may re-destroy already-freed slots.
Param user_context:

[in] User context.

Param key_id:

[inout] Key identifier to destroy.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*generate_key_pair)(void *user_context, void *decapsulation_key_id, uint8_t *encapsulation_key, size_t encapsulation_key_size, size_t *encapsulation_key_length)

Generate an ephemeral key pair (Initiator, message_1).

  The decapsulation (private) key stays in the key store as a
  handle; only the encapsulation (public) key leaves, to be sent
  in \c G_X. The handle is retained for \ref decapsulate and, on a
  NIKE suite, for the static-DH \c G_RX agreement in message_2
  (methods 1 and 3).
Param user_context:

[in] User context.

Param decapsulation_key_id:

[out] Handle of the generated private key.

Param encapsulation_key:

[out] Public key, sent in G_X.

Param encapsulation_key_size:

Size of the encapsulation_key buffer in bytes.

Param encapsulation_key_length:

[out] On success, the number of bytes that make up the encapsulation key.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*encapsulate)(void *user_context, const uint8_t *encapsulation_key, size_t encapsulation_key_length, void *decapsulation_key_id, void *shared_secret_key_id, uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_length)

Encapsulate to a peer’s ephemeral public key (Responder, message_2).

  • KEM suite: encapsulate against encapsulation_key (G_X), yielding the shared-secret handle and the ciphertext (G_Y).

  • NIKE (classical Diffie-Hellman) suite: the backend generates its own ephemeral key pair, runs the key agreement against encapsulation_key, and returns its own ephemeral public key as the ciphertext (G_Y).

decapsulation_key_id returns the Responder’s ephemeral private key so it can be reused for the static-DH G_IY agreement in message_3 (methods 2 and 3); a KEM without static-DH support leaves it a null handle.

Param user_context:

[in] User context.

Param encapsulation_key:

[in] Peer public key from G_X.

Param encapsulation_key_length:

Size of the encapsulation_key buffer in bytes.

Param decapsulation_key_id:

[out] Handle of the retained ephemeral private key. A KEM without static-DH support leaves it a null handle.

Param shared_secret_key_id:

[out] Handle of the shared secret (G_XY).

Param ciphertext:

[out] KEM ciphertext, sent in G_Y.

Param ciphertext_size:

Size of the ciphertext buffer in bytes.

Param ciphertext_length:

[out] On success, the number of bytes that make up the ciphertext.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*decapsulate)(void *user_context, const void *decapsulation_key_id, const uint8_t *ciphertext, size_t ciphertext_length, void *shared_secret_key_id)

Decapsulate a ciphertext (Initiator, after message_2).

Param user_context:

[in] User context.

Param decapsulation_key_id:

[in] Handle of the private key from generate_key_pair.

Param ciphertext:

[in] KEM ciphertext from G_Y.

Param ciphertext_length:

Size of the ciphertext buffer in bytes.

Param shared_secret_key_id:

[out] Handle of the shared secret (G_XY).

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*key_agreement)(void *user_context, const void *private_key_id, const uint8_t *peer_public_key, size_t peer_public_key_length, void *shared_secret_key_id)

Compute a static Diffie-Hellman shared secret (methods 1/2/3).

  Used only for static-DH authentication: \c G_RX in message_2 and
  \c G_IY in message_3. Each agreement pairs one party's static
  authentication key with the other's ephemeral key, so which of
  the two arguments is the static one depends on the local role.
  A suite without static-DH support fails with
  #EDHOC_ERROR_NOT_SUPPORTED.
Param user_context:

[in] User context.

Param private_key_id:

[in] Handle of the local private key, static or ephemeral.

Param peer_public_key:

[in] Peer public key (raw bytes), ephemeral or static.

Param peer_public_key_length:

Size of the peer_public_key buffer in bytes.

Param shared_secret_key_id:

[out] Handle of the shared secret.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*sign)(void *user_context, const void *private_key_id, const uint8_t *input, size_t input_length, uint8_t *signature, size_t signature_size, size_t *signature_length)

Generate a digital signature.

Param user_context:

[in] User context.

Param private_key_id:

[in] Handle of the signing key.

Param input:

[in] Full message to sign (not a digest).

Param input_length:

Size of the input buffer in bytes.

Param signature:

[out] Buffer where the signature is to be written.

Param signature_size:

Size of the signature buffer in bytes.

Param signature_length:

[out] On success, the number of bytes that make up the signature.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*verify)(void *user_context, const uint8_t *public_key, size_t public_key_length, const uint8_t *input, size_t input_length, const uint8_t *signature, size_t signature_length)

Verify a digital signature against a raw peer public key.

Param user_context:

[in] User context.

Param public_key:

[in] Peer public key (raw bytes).

Param public_key_length:

Size of the public_key buffer in bytes.

Param input:

[in] Full signed message (not a digest).

Param input_length:

Size of the input buffer in bytes.

Param signature:

[in] Buffer containing the signature to verify.

Param signature_length:

Size of the signature buffer in bytes.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*extract)(void *user_context, const void *ikm_key_id, const uint8_t *salt, size_t salt_length, void *prk_key_id)

EDHOC_Extract: derive a pseudorandom key handle from a salt.

Param user_context:

[in] User context.

Param ikm_key_id:

[in] Input keying material handle.

Param salt:

[in] Raw salt.

Param salt_length:

Size of the salt buffer in bytes.

Param prk_key_id:

[out] Output pseudorandom key handle.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*expand)(void *user_context, const void *prk_key_id, const uint8_t *info, size_t info_length, enum edhoc_key_usage usage, void *output_key_id)

EDHOC_Expand producing a key handle (handle space).

  The \p usage selects the policy of the created key, since a
  PSA / secure-element key must receive its policy at creation.
Param user_context:

[in] User context.

Param prk_key_id:

[in] Pseudorandom key handle.

Param info:

[in] CBOR-encoded info.

Param info_length:

Size of the info buffer in bytes.

Param usage:

Policy of the produced key.

Param output_key_id:

[out] Output key handle.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*expand_raw)(void *user_context, const void *prk_key_id, const uint8_t *info, size_t info_length, uint8_t *output, size_t output_length)

EDHOC_Expand producing raw output (keystream, IV, MAC, exporter).

Param user_context:

[in] User context.

Param prk_key_id:

[in] Pseudorandom key handle.

Param info:

[in] CBOR-encoded info.

Param info_length:

Size of the info buffer in bytes.

Param output:

[out] Raw output keying material.

Param output_length:

Requested output length in bytes.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*aead_encrypt)(void *user_context, const void *key_id, const uint8_t *nonce, size_t nonce_length, const uint8_t *additional_data, size_t additional_data_length, const uint8_t *plaintext, size_t plaintext_length, uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_length)

Encrypt with an AEAD.

Param user_context:

[in] User context.

Param key_id:

[in] AEAD key handle.

Param nonce:

[in] Nonce or IV to use.

Param nonce_length:

Size of the nonce buffer in bytes.

Param additional_data:

[in] Additional data that will be authenticated but not encrypted.

Param additional_data_length:

Size of the additional_data buffer in bytes.

Param plaintext:

[in] Data that will be authenticated and encrypted.

Param plaintext_length:

Size of the plaintext buffer in bytes.

Param ciphertext:

[out] Buffer where the authenticated and encrypted data is to be written.

Param ciphertext_size:

Size of the ciphertext buffer in bytes.

Param ciphertext_length:

[out] On success, the number of bytes that make up the ciphertext.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*aead_decrypt)(void *user_context, const void *key_id, const uint8_t *nonce, size_t nonce_length, const uint8_t *additional_data, size_t additional_data_length, const uint8_t *ciphertext, size_t ciphertext_length, uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_length)

Decrypt with an AEAD.

Param user_context:

[in] User context.

Param key_id:

[in] AEAD key handle.

Param nonce:

[in] Nonce or IV to use.

Param nonce_length:

Size of the nonce buffer in bytes.

Param additional_data:

[in] Additional data that will be authenticated but not encrypted.

Param additional_data_length:

Size of the additional_data buffer in bytes.

Param ciphertext:

[in] Buffer containing the authenticated and encrypted data.

Param ciphertext_length:

Size of the ciphertext buffer in bytes.

Param plaintext:

[out] Buffer where the decrypted data is to be written.

Param plaintext_size:

Size of the plaintext buffer in bytes.

Param plaintext_length:

[out] On success, the number of bytes that make up the plaintext.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*hash_init)(void *user_context, void **operation)

Begin a multipart hash operation.

  The operation object is owned by the backend and released by
  \ref hash_finish or \ref hash_abort.
Param user_context:

[in] User context.

Param operation:

[out] On success, backend hash operation.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*hash_update)(void *user_context, void *operation, const uint8_t *input, size_t input_length)

Add input to a multipart hash operation.

Param user_context:

[in] User context.

Param operation:

[in] Backend hash operation.

Param input:

[in] Input message chunk to hash.

Param input_length:

Size of the input buffer in bytes.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*hash_finish)(void *user_context, void *operation, uint8_t *hash, size_t hash_size, size_t *hash_length)

Finish a multipart hash operation and release it.

Param user_context:

[in] User context.

Param operation:

[in] Backend hash operation.

Param hash:

[out] Buffer where the hash is to be written.

Param hash_size:

Size of the hash buffer in bytes.

Param hash_length:

[out] On success, the number of bytes that make up the hash.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).

int (*hash_abort)(void *user_context, void *operation)

Abort a multipart hash operation and release it.

Param user_context:

[in] User context.

Param operation:

[in] Backend hash operation.

Retval EDHOC_SUCCESS:

Success.

Return:

Negative error code on failure (EDHOC error codes).