How to create a Hermes-based client-server app

Intro

1.Credential Store: a trusted source of user/service public credentials.

2.Data Store: a physical or logical unit for storing system data.

3.Keystore: a physical or logical unit for storing data access control keys.

These three components are considered to be Server side.

4.Client: an active entity in the architecture that produces or consumes the data.

All the system components need to be physically distributed to different servers for security reasons (Keystore and Data Store can theoretically be placed together).
Client and Server (as well as the Server components) need to have some form of communication between them (transport layer).

Note: A quick reminder that Hermes-core currently includes examples with tutorials in C, Python, and Go - you may choose to familiarise yourself with those first before proceeding.

Architecture and design

As an abstract framework, Hermes-core doesn't include any communication and storage components, only interfaces. Communication and storage entities (Data store, Credential store, Keystore) must be implemented before using Hermes-core.

Communication (Transport)

Transport is a means of providing connection and communication between the Server and the Client, as well as between the separate Server components.
There is only one requirement towards the communication between the components of Hermes-core — security.

For this reason, Hermes-core has a built-in wrapper that creates Themis'
Secure Session communication
channel under the abstract transport that needs to be implemented by the user. Such transport can be
created using any available mechanism and it must be able to implement the following interface
(include/hermes/rpc/transport.h):

typedefstructsecure_transport_type{// transport that will be wrappedhm_rpc_transport_t*user_transport;// secure session for this connectionsecure_session_t*session;secure_session_user_callbacks_t*session_callback;}secure_transport_t;uint32_tdestroy_secure_transport(secure_transport_t**transport_);uint32_tdestroy_rpc_secure_transport(hm_rpc_transport_t**transport_);hm_rpc_transport_t*create_secure_transport(constuint8_t*user_id,size_tuser_id_length,constuint8_t*private_key,size_tprivate_key_length,constuint8_t*public_key,size_tpublic_key_length,constuint8_t*public_key_id,size_tpublic_key_id_length,hm_rpc_transport_t*user_transport,boolis_server);hm_rpc_transport_t*create_secure_transport_with_callback(constuint8_t*user_id,size_tuser_id_length,constuint8_t*private_key,size_tprivate_key_length,secure_session_user_callbacks_t*callback,hm_rpc_transport_t*user_transport,boolis_server);

Сredential store

Hermes-core doesn't have special requirements towards the Credential store database. The database needs to be able to implement the following interface (include/hermes/credential_store/db.h):

typedefuint32_t(*hm_cs_db_get_pub_by_id_t)(void*db,constuint8_t*id,constsize_tid_length,uint8_t**key,size_t*key_length);typedefstructhm_cs_db_type{void*user_data;// get public key by provided user idhm_cs_db_get_pub_by_id_tget_pub;}hm_cs_db_t;

The service is a helper object, which launches an infinite loop after the start method has been called to receive a command, execute, send the result, repeat, etc. However, start is a blocking method, so for a more efficient implementation, each service needs to be created in a separate thread.

Data store and Keystore use Credential store to receive the public keys of clients that connect to them. So for processing the clients' requests, they use Credential store as a callback in Secure Session to receive the public key(s).
This means that a connection to Credential Store must be created:

What comes next is the initialization of the service with the transport being passed to it.

This procedure is also applicable for Keystore and Data store that use Credential Store in Secure Session. However, this process will be slightly different for Credential Store because it will have to use itself for the authentication of users:

Here a callback is explicitly created with a function for receiving the public key, which will use the Credential store's own mechanics (the 'db' object). The implementation of the functions for receiving the public key can be found here - src/secure_transport/session_callback.c. The rest of the process for initializing the service is similar to that described for Data store and Credential store.

To construct the mid_hermes_t instance function mid_hermes_create, in addition to the user id and the user private key, three transports instances to Credential store, Data store, and Keystore are needed respectively.

First, you need to connect to all the services of Credential store, Data store, and Keystore, and create a Secure Session between them using the Hermes wrapper (unless you want to use non-secure unencrypted open transports like TCP/UDP/Websocket or want to implement your own means of supporting encryption like TLS/SSL).

Transport needs to be wrapped into Secure Session by calling the create_secure_transport function from include/hermes/secure_transport/transport.h and passing the user's id, user's public key that will be used for establishing the session, ID of the service we're connecting to (in this case it is Credential store), the service's public key, and the transport that's being wrapped.

The type of connection that needs to be established must also be indicated here - either the server type (then the last parameter will be true) or the client type (the last parameter will be false).

The necessity to indicate the connection type is due to the fact that the session is always initialized by the Client who needs to send a request for establishing a session. You can read more here.

The connection with Data store and Keystore is created in a similar manner 1, 2.

Now the mid_hermes object can be created, requests to the API will be sent through it. When creating the mid_hermes object, the following parameters need to be passed - user_id and its private_key, as well as the 3 connections to the services, created earlier: