Color scheme: Riviera
You are here: start » en » Documentation » Post-Quantum Cryptography » WvdS Crypto Service overview » How-to guides » Call the L4Re service

Call the L4Re service

This guide shows how an L4Re task uses DataspaceEndpoint to have the service generate an ML-DSA key and sign a message with it. The examples are C++ because DataspaceEndpoint is a C++ class. They need only wvds_crypto.h and the DataspaceEndpoint header. The build_… functions that assemble messages are plain C and drop into C code unchanged.

Before you begin:

  • The service runs as a task of its own; see Deployment forms compared.
  • Your task has access to your platform vendor's DataspaceEndpoint and sets it up the way the vendor requires: registry server as a static variable, constructor on the main thread, server loop at the end of main.

1. Create the endpoint

#include "wvds_crypto.h"
#include "DataspaceEndpoint.h"
 
static L4Re::Util::Registry_server<> server;
 
int main()
{
    DataspaceEndpoint ep(&server,
                         "CRYPTO_CLI",   // your name, as the service expects it
                         "CRYPTO_SVC",   // the service's name
                         64 * 1024,      // size of the peer's region
                         5000000,        // peer timeout
                         on_crypto_response);
    // start a worker thread that calls wait_for_initialization() and then sends requests
    server.loop();
}

The names are fixed in the service. It won't find a task with a different name.

2. Have a key generated

static size_t build_ml_dsa_keygen(uint8_t *buf, uint8_t level)
{
    wvds_init_header((wvds_message_header_t *)buf, WVDS_REQ_ML_DSA_KEYGEN, 0, 1);
    WVDS_PAYLOAD_PTR(buf)[0] = level;
    return WVDS_MESSAGE_SIZE(1);
}
 
/* on the worker thread, after wait_for_initialization() */
uint8_t *buf = (uint8_t *)ep.allocate_local_blocking(WVDS_MESSAGE_SIZE(1));
size_t len = build_ml_dsa_keygen(buf, WVDS_ML_DSA_65);
ep.add_new_req(next_id++, WVDS_REQ_ML_DSA_KEYGEN, buf, len);

The response carries the new key ID in the first 4 bytes of the payload, followed by the public key, 1952 bytes for ML-DSA-65.

3. Have a message signed

static size_t build_ml_dsa_sign(uint8_t *buf, uint32_t key_id,
                                const uint8_t *msg, uint32_t msg_len)
{
    wvds_init_header((wvds_message_header_t *)buf, WVDS_REQ_ML_DSA_SIGN, key_id, 1 + msg_len);
    uint8_t *p = WVDS_PAYLOAD_PTR(buf);
    p[0] = WVDS_ML_DSA_65;            /* not evaluated; the level comes from the key */
    memcpy(p + 1, msg, msg_len);
    return WVDS_MESSAGE_SIZE(1 + msg_len);
}

The whole request, that is, the 16-byte header, the level byte, and the message, has to fit into the 64 KiB region the service maps from your task. Keep messages well below 64 KiB, and sign a hash for larger data. The response is the signature itself, exactly WVDS_ML_DSA_65_SIG_SIZE bytes for ML-DSA-65.

4. Handle responses

int on_crypto_response(DataspaceEndpoint *self, u_int64_t id, u_int8_t type,
                       const u_int8_t *data, l4_size_t size)
{
    wvds_message_header_t hdr;
 
    if (size >= sizeof hdr) {
        memcpy(&hdr, data, sizeof hdr);
        const uint8_t *payload = data + sizeof hdr;
 
        if (!wvds_validate_header(&hdr) || sizeof hdr + hdr.payload_len != size) {
            /* not a valid response from the service */
        } else if (wvds_is_error_response(data, size)) {
            uint32_t code;
            memcpy(&code, payload, sizeof code);
            /* wvds_error_name((wvds_error_code_t)code) returns the name */
        } else {
            /* evaluate the content based on the pending request with this id */
        }
    }
 
    self->free_peer_req(id, type, data, size);   /* always release */
    return 0;
}

Three things make an integration robust:

  • Errors live in the header. The IPC type of a response is always 0x80. Only req_type in the header tells you whether it's an error; wvds_is_error_response checks exactly that.
  • Always release. Call free_peer_req for every response, including invalid ones. Otherwise the service's region fills up and it can no longer respond.
  • Plan for timeouts. Under overload, the service drops requests without a response. Track outstanding id values and give up on a request after a deadline.
en/wiki/pqcrypt/svc/how-to/call-l4re-service.txt · Last modified: by Wolfgang van der Stille