Matomo

Implementing Hermes-based Security Systems | Cossack Labs

🇺🇦 We stand with Ukraine, and we stand for Ukraine. We offer free assessment and mitigation services to improve Ukrainian companies security resilience.

Implementing Hermes-based Security Systems

Table of Contents

  1. Introduction
  2. Structure
  3. Goals
  4. Part 1. Hermes reference implementation
  5. Part 2. Implementation considerations
  6. Part 3. Adding features & further work
  7. Part 4. Applications / examples

Introduction #

In Hermes – a framework for cryptographically assured access control and data security document we have proposed a practical security scheme called Hermes. This scheme can be appropriately applied in cases when we want to enhance the confidence in infrastructures that use third-party remote data storage/processing services.

Hermes is designed in such a way that the plain-text sensitive data is processed only within the client’s context, while the server operates with the encrypted sensitive data and doesn’t need to decrypt the data to perform the basic persistent storage operations (CRUD). Hermes also provides tools for distributing and enforcing data access control via cryptographic methods (the level of data access is determined by the possession or the absence of one or another cryptographic key).

Structure #

This paper consists of four parts:

Part 1: Provides a description of our reference implementation of Hermes as well as its security and performance analysis.

Part 2: Devoted to the practical features of introducing Hermes into an existing infrastructure.

Part 3: Offers and explains some possible ways to provide execution and observation of security assumptions about the operational process of Hermes, described in Hermes – a framework for cryptographically assured access control and data security.

Part 4: Describes our real-world applications that use Hermes and also provides brief info about the future work and research.

Goals #

The main goal of this paper is to provide answers to the following questions:

  1. How does Hermes-based security system work in a real-world setting?
  2. How to implement a Hermes-based security system?
  3. How to add features into the reference implementation of a Hermes security scheme?
  4. What additional services should be provided for the most secure and convenient functioning of a Hermes-based security system?

Part 1. Hermes reference implementation #

The reference proof-of-concept implementation of Hermes is a software library ‘hermes-core’ (hereinafter referred to as Hermes-core). You can obtain the source code of Hermes-core through installing it from Cossack Labs’ repository or by downloading and building it from the source code.

Hermes-core library contains all the necessary functionality for implementing Hermes-based security system in an existing infrastructure (the infrastructure itself is not implemented, but the library provides the necessary interfaces for it) and for implementation of the Remote Procedure Call (RPC) technology, which is necessary for correct interpretation of the messages between the components.

1.1. Implementation of cryptographic operations #

According to Hermes – a framework for cryptographically assured access control and data security, Hermes uses 3 low-level cryptographic operations:

  • Symmetric encryption;
  • Asymmetric Diffie-Hellman-based key wrapping scheme;
  • Message authentication code.

To implement these structures, we use our convenient cryptographic library Themis. To implement the authentication of all channels of communication, we use the Secure Session cryptosystem from Themis.

After installing (building) Themis from source with all the default settings and components, Themis (and, consequently, Hermes) uses the following cryptographic primitives:

  • Block cipher AES in GCM mode with 256 bit key,
  • Key derivation function from RFC6189 – section 4.5.1 (KDF),
  • Elliptic curve Diffie-Hellman protocol with curve NID_X9_62_prime256v1 (ECDH-256),
  • Message authentication code HMAC (HMAC-SHA256), and
  • Hash function SHA-256.

Hermes can be built with different primitives, as long as they are providing comparable security guarantees and satisfy the basic assumptions for Hermes (described in Hermes – a framework for cryptographically assured access control and data security).

1.2. Implementation of components #

Hermes includes 4 components: Client(s), Data store, Keystore, and Credential store. In fact, the reference implementation of Hermes can be conditionally split into 4 basic sets of functions, where each set includes the functions that define the necessary abilities and properties of each component.

The architectural scheme of Hermes-core is described in the following diagram:

hermes architectural scheme

The functional scheme of Hermes is described in the following diagram:

hermes functional scheme

The main functions that define high-level interfaces for each component are listed below:

The Client(s) #

The Client’s interface:

  • Provides CRUD-like API for operations on protected data;
  • Allows to grant/revoke permissions to perform CRUD operations on data to/from other Clients.

Note that the Client is the only component that is able to operate with sensitive data in plain text. From the security’s point of view, it is the Client’s interface that includes the most important functions. A detailed description of such functions is provided in Hermes – a framework for cryptographically assured access control and data security, while the implementation precisely matches the description.

Data store #

Data store’ interface:

  • Puts data into storage;
  • Modifies data (authorises modification);
  • Extracts data from storage;
  • Deletes data from storage.

The Data store is necessary for storage of encrypted sensitive data. Hermes operates with data represented as records — minimal atomic blocks of data. Terms “records” and “blocks” are used interchangeably when the Hermes-core implementation of Hermes is discussed. Representation of data as records is arbitrary and is defined by the Client. Each record (during the initial writing to the Data store) has two additional service data blocks: record ID and Update Tag (UT). The record ID is used for indexing, while UT is used for authorisation of data modification.

When the Client tries to establish a connection, the Data store verifies the presence of the Client’s public credential in the Credential store. If it fails, the Data store rejects the requested operation.

The Data store operations are based on blocks, private metadata, record’s MAC and (in the cases where the protected data is changed) MAC of the previous state of a record.

For the UPDATE operations, upon receiving the abovementioned data, the Data store will verify that the Client performing the UPDATE possesses a valid UPDATE key by comparing the current Update Tag (MAC) received from the Client with the stored Update Tag (MAC) associated with the block. If they are equal, the Data store will overwrite the stored encrypted block with the received encrypted updated block and the stored Update Tag with a new Update Tag. This way the Data store never processes the blocks in plain text.

Hermes-core doesn’t have special requirements towards the Data store’s database. The database API only needs to be able to implement the following interface:

    // insert block to data store. Will return id of added block
    typedef uint32_t(*hm_ds_db_insert_block)(
            void* db, const uint8_t* block, const size_t block_length,
            const uint8_t* meta, const size_t meta_length,
            const uint8_t* mac, const size_t mac_length,
            uint8_t** id, size_t* id_length);

    // insert block to data store with predefined id. if block with provided id is already present in data store error will return
    typedef uint32_t(*hm_ds_db_insert_block_with_id)(
            void* db, const uint8_t* id, const size_t id_length,
            const uint8_t* block, const size_t block_length,
            const uint8_t* meta, const size_t meta_length,
            const uint8_t* mac, const size_t mac_length);

    // read block from data store
    typedef uint32_t(*hm_ds_db_get_block)(
            void* db, const uint8_t* id, const size_t id_length,
            uint8_t** block, size_t*  block_length,
            uint8_t** meta, size_t*  meta_length);

    // update block in data store 
    typedef uint32_t(*hm_ds_db_update_block)(
            void* db, const uint8_t* id, const size_t id_length,
            const uint8_t* block, const size_t block_length,
            const uint8_t* meta, const size_t meta_length,
            const uint8_t* mac, const size_t mac_length,
            const uint8_t* old_mac, const size_t old_mac_length);

    // delete block from data store
    typedef uint32_t(*hm_ds_db_rem_block)(
            void* db, const uint8_t* id, const size_t id_length,
            const uint8_t* old_mac, const size_t old_mac_length);

    typedef struct hm_ds_db_type{
        void* user_data;
        hm_ds_db_insert_block insert_block;
        hm_ds_db_insert_block_with_id insert_block_with_id;
        hm_ds_db_get_block get_block;
        hm_ds_db_update_block update_block;
        hm_ds_db_rem_block rem_block;
    }hm_ds_db_t;

A simple filesystem-based data storage implementation can be found in: docs/examples/c/mid_hermes/data_store_service/db.h and docs/examples/c/mid_hermes/data_store_service/db.c.

Keystore #

The Keystore’s interface:

  • Puts data to storage: recording ACP;
  • Extracts data from storage: retrieving ACP to use it;
  • Deletes data from storage: removing ACP data.

The Keystore is necessary for storing the wrapped symmetric keys that are used for encryption of sensitive data. The mechanism of wrapping/unwrapping is described in Hermes – a framework for cryptographically assured access control and data security. A reference implementation represents each wrapped symmetric key as a so-called ‘token’. A token contains an actually wrapped symmetric key value, the ID of the Client who granted this token, and the ID of the Client to whom this token was granted.

When a Client tries to establish a connection, the Keystore verifies the presence of the Client’s public credential in the Credential store. If it fails, the Keystore refuses to perform the requested operation.

The Keystore operates with these fields:

  • block ID — ID of the data block (record) for which the token is designated,
  • user ID — ID of the user (Client) for which the token is designated,
  • owner ID — ID of the user who issued the token,
  • key/token type (read or update) — type of the permissions granted with the token,
  • key/token — the actual key/token.

Each access control key (‘token’) is stored as a combination of separate tokens: block ID + user ID + token + owner ID.

To be able to use the token, a user with the user ID needs his/her private key and a public user key with the owner ID.

Hermes-core doesn’t have special requirements towards the Keystore database. The database API needs to be able to implement the following interface:

    // set token
    typedef uint32_t(*hm_ks_db_set_token)(
            void* db, const uint8_t* block_id, const size_t block_id_length,
            const uint8_t* user_id, const size_t user_id_length,
            const uint8_t* owner_id, const size_t owner_id_length,
            const uint8_t* read_token, const size_t read_token_length);

    // get token
    typedef uint32_t(*hm_ks_db_get_token)(
            void* db, const uint8_t* block_id, const size_t block_id_length,
            const uint8_t* user_id, const size_t user_id_length,
            uint8_t** write_token, size_t* write_token_id_length,
            uint8_t** owner_id, size_t* owner_id_length);

    // delete token
    typedef uint32_t(*hm_ks_db_del_token)(
            void* db, const uint8_t* block_id, const size_t block_id_length,
            const uint8_t* user_id, const size_t user_id_length,
            const uint8_t* owner_id, const size_t owner_id_length);

    // return user id and user rights mask ("r" or "w") with shift by index 
    typedef uint32_t(*hm_ks_db_get_indexed_rights)(
            void* db, const uint8_t* block_id, const size_t block_id_length,
            const size_t index, uint8_t** user_id,
            size_t* user_id_length, uint32_t* rights_mask);

    typedef struct hm_ks_db_type{
        void* user_data;
        // set read token
        hm_ks_db_set_token set_rtoken;
        // set update token
        hm_ks_db_set_token set_wtoken;

        // get read token
        hm_ks_db_get_token get_rtoken;
        // get update token
        hm_ks_db_get_token get_wtoken;

        // return user id and user rights mask ("r" or "w") with shift by index 
        hm_ks_db_get_indexed_rights get_indexed_rights;

        // delete read token
        hm_ks_db_del_token del_rtoken;
        // delete update token
        hm_ks_db_del_token del_wtoken;
    }hm_ks_db_t;

A simple filesystem-based Keystore implementation can be found in the following examples: docs/examples/c/mid_hermes/key_store_service/db.h and docs/examples/c/mid_hermes/key_store_service/db.c

Credential store #

The Credential store’s interface:

  • Puts data into storage: performs user registration;
  • Extracts data from storage: matches ID with a public key;
  • Deletes data from storage: removes user’s public key.

The Credential store is necessary for storing public credentials of each entity in a Hermes-based system. There is an ID stored in the Credential store along with each public credential. The registration of new Clients (the process of writing Clients’ public credentials to the Keystore) is performed by an administrator of the Hermes-based system.

A typical implementation of credential store is X.509 certificate authority / PKI server.

The Credential store is used for retrieving public keys by the user identifier (user ID). It can be as simple as a function or a table that maps public keys to the user IDs to a full-blown local certificate authority.

Hermes-core doesn’t have special requirements towards the Credential store. The Credential store API needs to be able to implement the following interface:

    // get public key by provided user id
    typedef uint32_t(*hm_cs_db_get_pub_by_id_t)(void *db, const uint8_t *id, const size_t id_length, uint8_t **key, size_t *key_length);

    typedef struct hm_cs_db_type {
        void *user_data;
        hm_cs_db_get_pub_by_id_t get_pub;
    } hm_cs_db_t;

A simple filesystem-based Credential store implementation can be found in the following examples: docs/examples/c/mid_hermes/credential_store_service/db.h and docs/examples/c/mid_hermes/credential_store_service/db.c

1.3. Usage and examples #

Hermes-core is provided with an example implementation of an end-user application that enables users to securely store and share the data.

The Data store, the Keystore, and the Credential store are implemented in C code (https://github.com/cossacklabs/hermes-core/tree/master/docs/examples/c), with additional clients in Go (https://github.com/cossacklabs/hermes-core/tree/master/docs/examples/go) and Python (https://github.com/cossacklabs/hermes-core/tree/master/docs/examples/python).

A typical usage flow for examples is documented in the tutorial section (Python tutorial, C tutorial, Go tutorial) of the Hermes-core documentation, and consists of:

  • Generating user keypairs;
  • Running all the server-side processes providing store APIs to the Client;
  • Creating documents and distributing access (“working” with the protected data).

This minimalist example illustrates all the processes Hermes is able to provide to a consumer application. In the Part 4of this document we outline the demonstration applications we’ve created to show how to integrate Hermes into real-world use-cases.

1.4. Tested on #

Hermes-core was tested to build on the following operational systems:

  • Centos: 7 (x86_64);
  • Debian: Stretch, Jessie, Wheezy (amd64 / i386);
  • Ubuntu: Zesty, Yakkety, Xenial, Trusty.

Hermes-core has high-level wrappers to make Hermes API available on:

  • C;
  • GoLang v.1.9;
  • Python 2.7.12, 3.3.6, 3.4.4, 3.5.3, 3.6.2.

1.5. Performance analysis #

By providing a reference implementation, we’re setting a baseline for further Hermes implementations and an analysis of their performance. Such analysis is based on the total amount of “atomic” cryptographic operations needed for performing one or another of Client’s operations.

The following cryptographic operations are used as “atomic” in the Hermes’ context:

Name of the operation Abbreviation Speed
Ephemeral asymmetric key-pair generation EAKPG slow
Symmetric key generation SKG very fast
Symmetric encryption/decryption SE/D very fast
Key derivation KD fast
Diffie-Hellman key exchange DHKE slow
Mutual authentication MA very slow
MAC calculation MACC fast

Now, we can measure the complexity of each individual CRUD operation as well as operations on manipulating the already existing CRUD permissions (over one record). The notation used in the table below is as follows:

  • R - single record;
  • C - client grants or revokes access to R;
  • B - client that obtains permissions from C
  • DS - Data store;
  • KS - Keystore;
  • CS - Credential store.
Operation High-level representation (see CRUD implementation section in the scientific paper on Hermes) “Atomic” representation
CREATE 1) C establishes Secure Session with DS MA - 1
2) DS establishes Secure Session with CS and verifies C MA - 1
3) C encrypts R and puts it into DS SKG - 2, SE - 1, MACC - 1
4) C establishes Secure Session with CS and gets own public key MA - 1
5) C establishes Secure Session with KS MA - 1
6) KS establishes Secure Session with CS and verifies C MA - 1
7) C wraps 2 symmetric keys and put them into KS KD - 2, EAKPG - 2, DHKE - 2
READ 1) C establishes Secure Session with KS and gets wrapped symmetric keys MA - 1
2) KS establishes Secure Session with CS and verifies C MA - 1
3) C establishes Secure Session with DS and gets encrypted data MA - 1
4) DS establishes Secure Session with CS and verifies C MA - 1
5) C unwraps wrapped symmetric key KD - 1, DHKE - 1
6) C decrypts record SE - 1
UPDATE 1) C establishes Secure Session with KS and gets wrapped symmetric keys MA - 1
2) KS establishes Secure Session with CS and verifies C MA - 1
3) C establishes Secure Session with DS and gets encrypted data MA - 1
4) DS establishes Secure Session with CS and verifies C MA - 1
5) C unwraps 2 wrapped symmetric keys KD - 2, DHKE - 2
6) C decrypts R SE - 1
7) C calculates old UT MACC - 1
8) C calculates new UT MACC - 1
9) C encrypts updated record and puts it into DS SE - 1
DELETE 1) C establishes Secure Session with KS and gets wrapped symmetric keys MA - 1
2) KS establishes Secure Session with CS and verifies C MA - 1
3) C establishes Secure Session with DS and gets encrypted data MA - 1
4) DS establishes Secure Session with CS and verifies C MA - 1
5) C unwraps 2 wrapped symmetric keys KD - 2, DHKE - 2
6) C decrypts R SE - 1
7) C calculates old UT MACC - 1
8) C calculates new UT MACC - 1
Grant READ 1) C establishes Secure Session with CS and gets public key of B MA - 1
2) C establishes Secure Session with KS MA - 1
3) KS establishes Secure Session with CS and verifies C MA - 1
4) C unwraps one wrapped symmetric key KD - 1, DHKE - 1
5) C wraps one symmetric key and put it into KS KD - 1, EAKPG - 1, DHKE - 1
Grant UPDATE 1) C establishes Secure Session with CS and gets public key of B MA - 1
2) C establishes Secure Session with KS MA - 1
3) KS establishes Secure Session with CS and verifies C MA - 1
4) C unwraps one wrapped symmetric key KD - 1, DHKE - 1
5) C wraps one symmetric key and put it into KS KD - 1, EAKPG - 1, DHKE - 1
Revoke READ 1) C establishes Secure Session with KS MA - 1
2) KS establishes Secure Session with CS and verifies C (then C “remembers the presence” of current wrapped READ ACKs to specified record and then deletes them from Keystore) MA - 1
3) C re-encrypts R with new READ ACK SKG - 1, SE - 2
4) C wraps new READ ACK for each client who should have READ access to R (let’s denote as n) and puts all wrappings of READ ACK into KS KD - n, EAKPG - n, DHKE -
5) C establishes Secure Session with DS MA - 1
6) DS establishes Secure Session with CS and verifies C (then C puts encrypted R into DS) MA - 1
Revoke UPDATE 1) C establishes Secure Session with KS MA - 1
2) KS establishes Secure Session with CS and verifies C (then C “remembers the presence” of current wrapped UPDATE ACKs to specified record and then deletes them from Keystore) MA - 1
3) C establishes Secure Session with DS MA - 1
4) DS establishes Secure Session with CS and verifies C MA - 1
5) C unwraps UPDATE ACK KD - 1, DHKE - 1
6) C generates new UPDATE ACK SKG - 1
7) C calculates old UT MACC - 1
8) C calculates new UT (then C updates R in DS) MACC - 1
9) C wraps new UPDATE ACK for each client who should have UPDATE access to R (then, C puts all wrappings of UPDATE ACK into KS) KD - n, EAKPG - n, DHKE - n

One can see that the most complex operation is a record R permission revoking, because it affects all the Clients related to R.

1.6. Security analysis #

Ways to break Stores #

There is a standard approach to modelling threats to distributed storage systems (to which the Data store, the Keystore, and the Credential store belong) based on the classical security principles (Confidentiality, Integrity, Availability, Authentication, or CIAA). Here we briefly discuss the types of attacks from the CIAA model that are applicable to Hermes. For more details, including the data lifecycle thread model process, see the “Toward a Threat Model for Storage Systems” article.

Confidentiality Attacks. Confidentiality attacks attempt to READ the information from a storage system, without proper authorisation. If an attacker gains administrator-level access to the system, they can typically explore storage with few safeguards. However, an attacker may also READ the information without an illegitimate privilege escalation (i.e. insider attackers). Storage leaks via covert channels also fall into this group.

There is a number of variants of confidentiality attacks:

  • Sniffing storage traffic;
  • Snooping on buffer cache;
  • Snooping on deleted storage blocks;
  • Snooping on deallocated memory;
  • File system profiling.

Hermes prevents confidentiality attacks through encrypting the protected content. However, when a successful confidentiality attack is combined with other attacks, adversaries can corrupt data, trigger denial of service, and affect the access policy distribution (more on that in “Hermes – a framework for cryptographically assured access control and data security”).

Integrity Attacks. Integrity attacks attempt to modify the information in a storage system without proper authorisation. Such modification may include creating, changing, appending, writing, and deleting both the data and the metadata.

There is a number of variants of integrity attacks:

  • Storage jamming;
  • Modifying metadata;
  • Subversion attacks;

Integrity attacks on stores can lead to denial of service, and, under special circumstances, combined with other attacks and compromises, impersonation.

Availability Attacks. Availability attacks attempt to make data or storage services unavailable for a period of time. The data (or metadata) and the storage services must be available on-demand to legitimate parties when requested. Denial-of-service (DoS) attacks try to make the data and the storage services unavailable by exhausting the resources through legitimate storage mechanisms, which makes this attack the hardest to prevent.

There is a number of variants of availability attacks:

  • Exhausting log space;
  • Exhausting data blocks;
  • Exhausting metadata space;
  • Creating redundant versions;
  • Exhausting file handles;
  • Flash memory attacks;
  • Attacks on storage-related OS structures;
  • Fragmentation attack;
  • Deletion of data;
  • Network disruption;

Availability attacks are out of scope of a cryptographic security system. Even though they lead to a devastating effects for the protected data, the availability should be protected by other means (data redundancy, backups, etc.).

Authentication Attacks. Authentication attacks occur when an attacker poses as a legitimate user (using a purloined password or credential) or when an attack storage device poses as a legitimate storage device. For instance, an impersonator can launch insider attacks to access data/metadata (confidentiality), modify data/metadata (integrity), and/or deny others data/metadata (availability) based on the legitimate user identity authorization capabilities they have taken over. Man-in-the-middle attacks are another attack vector on authentication.

There is a number of variants of authentication attacks:

  • Storage user impersonation;
  • Storage device impersonation.

With Hermes, authentication attacks can only be implemented from the Clients’ side. In narrow use-cases, some authentication attacks can be successfully mounted by compromising multiple stores and accumulating additional data from users, which is discussed in more detail in the formal paper on Hermes.

1.7. Ways to break channel trust #

The security of network communications is rather important due to the particular architectural features of Hermes, wherein authenticity has higher priority than confidentiality. Traditionally, the threat model for secure communications includes passive and active attacks. Passive attacks take the form of eavesdropping and fall into two categories: reading the contents of a message or — more subtly — analysing patterns of traffic to infer the nature even of the secure messages. These attacks are hard to detect so the emphasis is usually on their prevention. Active attacks involve modification of a data stream or creation of a false data stream. One entity may masquerade as another (presumably one with more or different privileges), it may be by capturing and replaying an authentication sequence. For more details, see these lecture notes on OS Protection and Security.

The network communications can be vulnerable to the following types of attacks (we also advise you to see this article on Common Types of Network Attacks):

  • Eavesdropping — an attacker gains access to the data paths in the network to “listen in” or to interpret (read) the traffic;
  • Data Modification — after an attacker has read your data, the next logical step is to alter it;
  • Identity Spoofing (IP Address Spoofing) — an attacker uses special software to construct IP packets that appear to originate from valid addresses inside the corporate intranet;
  • Password-Based Attacks — if the access rights to the network resource are defined by the knowledge of a password and the identity information is passed through the network unprotected, this might allow an eavesdropper to gain access to the network by posing as a valid user;
  • Denial-of-Service Attacks — unlike a password-based attack, the denial-of-service attack prevents normal use of the network by valid users;
  • Man-in-the-Middle Attack — man-in-the-middle attacks occurs when someone between you and the person with whom you are communicating is actively monitoring, capturing, and controlling your communication transparently;
  • Compromised-Key Attack — if an attacker can obtain an encryption key in some way, they can use it to gain access to a secured communication without the sender or receiver being aware of the attack;
  • Sniffer Attack — an attacker can analyse the network and gain information to eventually cause this network to crash or to become corrupted, or read communications with a help of special sniffing software;
  • Application-Layer Attack — targets application servers by deliberately causing a fault in a server’s operating system or applications.

In general, an active or passive compromisation of Hermes’ trust model only leads to leakage of encrypted text or denial of service.

1.8. Ways to break Client #

The architecture of Hermes implies that only the Client operates with plaintext sensitive data, so security of the Client’s environment is crucial. Hermes’ Client software executes within a context of an operating system (OS) and the most sophisticated threats to OSs arise through malicious software (malware).

While deploying a security system built around Hermes methodology, it is important to consider:

  • Ways of ensuring trust to code that performs encryption;
  • Ways of ensuring trust to code that has contact with plaintext data;
  • Ways of ensuring key security if the keypair is stored in the open;
  • Ways of ensuring password security if the keypair is protected with a password.

Part 2. Implementation considerations #

This section provides things to consider when building Hermes-based applications. We will discuss ways to represent data, manage rights, and build practical infrastructure around Hermes’ processes.

2.1. Storage model and considerations #

Objects and groups of objects #

The reference implementation of Hermes operates with discrete list of objects (records or blocks). Such atomic nature allows Hermes to operate in any storage environment. In reality, however, it is frequently the case of real-world systems operating on non-flat structured groups of records. For example, while considering a database instance as a set of tables, a table as a set of rows and a row as a set of cells — suddenly, there are three steps in the hierarchy. Looking at the document storage model, there’s even more depth to it: any JSON document is also a hierarchy of fields and each of these fields can be considered as a record. The ultimate point of this progression is a file system — a near-infinite restricted directed acyclic graph (without touching upon the subject of links).

Such hierarchy provides additional challenges for cryptographic access control systems — for managing access to groups of objects. One typical approach is to control access to the group of objects with the same key. But this breaks the “maximum compartmentalisation” approach behind the Hermes’ framework.

We propose a different approach, which is a subject of further work in Hermes-based systems. We implement an “index list”, which features all the “registered” records to control who has the right to manipulate records in chosen group of records (level in the storage hierarchy).

Component 1. Hierarchical data model

To provide atomic operations, Hermes has to be agnostic about the graph linkage scheme, but a typical approach is to define a “parent node” for each group of objects, this way linking groups of objects into a hierarchy.

Component 2. Permissions to read/write a record from a group

To get READ or WRITE access rights to a record, a two-step procedure is suggested:

  • Distribute the READ permissions to the “index list” between the appropriate users (a permission to list all the records in a current group);
  • Distribute the corresponding type of permissions to the records themselves.

This way, a Client/user has to gain access rights to a list of objects to be able to perform an operation on an object, which is then considered to be and regulated as a normal atomic Hermes operation.

Component 3. Permissions to create a record (changing a list of records)

Granting the WRITE access rights to this index list is equal to granting the right to CREATE and DELETE (although DELETE also requires wiping the record itself, which is a different subject matter) records within this group.

By combining these three steps, implementing infinite graphs is possible.

Additional theoretical developments on handling graphs and trees in storage are presented in the Part 3 of this document.

Locking and race conditions #

Given that Hermes requires two separate operations to change the permissions/data in its store, a problem of commit synchronisation occurs.

A practical implementation of a Hermes-based system at a scale would either involve custom write locks or multi-phase commits to ensure consistency while changing access rights or data. Writing into two stores simultaneously leads to a number of data risks: what if one of writes fail? What if they succeed but are corrupt? Making multi-phase commit? Or locking everything until both of the values (Data store/Keystore) are recorded. This might become challenging in some systems.

2.2. Public records #

In Hermes, the Data store can store both the encrypted and unencrypted data. This means that the Hermes’ documents can contain public records — some information that is unencrypted and widely available. For instance, such public records can be represented by the metadata used for indexing or for SQL/query filtering purposes.

It’s possible to hit all the necessary data (encrypted and plain) in one query.

2.3. Architectural considerations #

There are various architectural layouts that can be implemented using Hermes as a security engine:

  • Single point: the Client and Store APIs are implemented in one piece of code, some actual stores are combined together. This is useful for cases where Hermes is used to compartment cryptographic access via one trusted point (where the code runs).
  • Outsourcing Credential store: Hermes was built to blend well with modern PKI infrastructures (due to the simplicity of the API needed by the Credential store). It is worth noting that additional services provided by the modern CA/PKI servers could be used to harden the security of Hermes-based systems / mitigate some of the risks (see “Hermes – a framework for cryptographically assured access control and data security”).

Combining Data store + Keystore into one storage/database instance. Although intuitively it might look OK to store the data and the ACP altogether, combining the risks of the Data store and the Keystore compromisation increases the severity of the possible damage — the attackers can immediately benefit from altering parts of the ACP.

2.4. Client trust #

One of the core assumptions of Hermes is that the environment, which runs the client code, is trusted. To ensure such trust in real-world situations, apart from the standard measures for securing the Client, some additional efforts can be made:

  • The keys stored on the Client side should be encrypted with a password so that only the users’ active input grants the Client a right to decrypt the keys.
  • Execution flow should be protected through proper memory management where applicable.

To minimise the opportunity for active MiTM attacks and impersonation caused by Сlient environment compromisation taking place, continuous authentication of Client-Server connection should be implemented (that might include running Zero Knowledge Protocol on some of the session data).

Part 3. Adding features & further work #

Hermes solves a number of security risks as a cryptographic design. Hermes was designed to be data security / access control module of a larger system, namely Toughbase. It provides a number of security services and guarantees as it is, but works best in a richer, more developed environment, i.e. a large-scale application infrastructure or data storage and distribution solution.

This section outlines some of approaches, methods, tools, and services that take the Hermes-based security to a next level.

To enable efficient search on data within a database, indexing is frequently used. Indexing of the sensitive data is a security challenge that people attempt to solve with searchable encryption schemes like searchable symmetric encryption (SSE) and fully homomorphic encryption (FHE). All of them have either performance or security compromises.

There are other ways to solve this problem. These are the classic approaches:

  • Replacing data that needs to be indexed with a secure random token and adding proxy before the database (our product Acra works in a similar manner), where the requesting side (app) sends queries with sensitive data (i.e. e-mail), and the proxy (API endpoint for an app) replaces that for tokens, which are indexed openly and are used in JOINs/WHEREs.
  • Considering search to be a risky process and only revealing non-sensitive fields to the search indexer.
  • Making search indexer access the protected data as a Hermes’ Client, which is impractical as it results in slow index update performance.

Additionally, using SSE or deterministic encryption as a custom data type within Hermes solves this problem, but only to a limited extent.

3.2. Logging #

In one test project, we’ve implemented logging as a special Hermes document, where each log entry is a Hermes record shared with some “log-generating entity” that is formed by a Client’s process and is appended to a list of records. This way, an audit log is cryptographically proven, generating non-decryptable log raises intrusion alarm in case of an alteration of a normal application flow.

3.3. Zero knowledge proofs #

Hermes was built to protect structured data accumulated around some “unifying” ID. In certain situations, even the request of the ID itself can reveal some sensitive data (i.e. a SSN request to the Credit History Bureau reveals to the Credit History Bureau that a person identified by that SSN was somehow engaged with the requesting entity, i.e. a bank). For protecting the request from active adversary risks, we suggest replacing a sensitive public ID (SSN) with a secure random token with a limited lifetime (to mitigate the repeat attack), distributing it instead of a sensitive ID, then running a ZKP-based authentication system like Secure Comparator on an actual SSN to authorize the query.

3.4. Monitoring #

Cryptography is not a silver bullet — it just narrows the risk down to a controlled perimeter. There are several things worth monitoring for intrusions:

  • Access patterns to stores;
  • Requests in data storage solutions;
  • Client code execution environment.

3.5. Hardening additional privileges and graph storage #

The current scheme of handling documents, file systems, and infinite graphs is a proof-of-concept model. To deliver better performance, we’re investigating the possibility to:

  • Link “node” (i.e. a pointer to a list of records) record IDs to CREATE permissions/access rights;
  • Use special UPDATE Tag (GRANT Tag) as an access control token for CREATE/UPDATE transactions on the Keystore.

This research is omitted in the current presentation of the reference implementation of Hermes-core to provide a clearer view of the core technology.

3.6. Low-trust clients #

To deliver Hermes to thin clients that cannot be trusted to run cryptographic code (i.e. web browsers in their modern code trust state), we are developing a proxy, which will run an instance of Hermes in the cloud. It will implement the Client code of Hermes and, upon provision of a user’s password, it will decrypt the stored keypair and proxies’ requests back and forth to those low-trust clients.

3.7. Challenges for key rotation / re-keying #

As we’ve described in “Hermes – a framework for cryptographically assured access control and data security”, the key rotation requires two operations: writing a new record and writing keys. How will other clients gain the access to the keys in parallel?

There are several approaches towards handling this:

  • Access locking (valid for real-time systems where most of the users with most of the keys are present most of the time);
  • Record versioning, which will affect redundancy and involve additional procedures (wiping records on compaction cycle);
  • Key versioning and operating key chains instead of keys, which will affect redundancy and performance, yet is well studied in the enterprise data management systems.

3.8. Key escrow-like techniques #

What if a sole owner of a sensitive record leaves the system? Traditional key escrow techniques can be used in a Hermes-based system in a fashion similar to building key escrow for PKI-based encryption.

3.9. Further work #

This document is an ever-evolving and improving “work in progress”.

After outlining the possible directions for further methodology development, we’re focusing on building practical infrastructures for real-world applications in our secure distributed data store, Toughbase.

Reports on deploying Hermes and Toughbase will be used to extend this paper.

Part 4. Applications / examples #

This section is a growing list of examples of Hermes-based applications and potential use-cases we become aware of as we engage in in-depth discussions with early adopters.

Trying something interesting? Let us know and we’ll link you here.

4.1. Our real-world example applications #

  • MongoDB Proxy
  • Git
  • REST Proxy
  • iOS Record sharing app

4.2. Typical use cases #

The typical cases for which Hermes would be the most beneficial:

Cryptographic access control #

Hermes can be deployed for building cryptographic access control in your application. With its help you can regulate the READ and WRITE access rights through a cryptographic scheme resistant to privilege escalation.

Secure collaboration and data sharing #

Hermes is a cryptography-based method of providing protected data storage and sharing that allows the enforcement of cryptographically-checked permissions between any number of Hermes clients.

Multi-user object store #

Using Hermes, you can build end-to-end secure document/object stores where every document or field’s access rights can be granted to any registered user of the system, transparently, and with low overhead.

A typical real-world use-case for Hermes is an online medical service where the patients securely interact with the staff of multiple medical institutions and a patient’s records contain a number of results for medical examinations. The patient’s personal physician needs to be able to access all of them, while the employees of the medical institutions need to be able to access only the latest results to adjust their activities towards the patient. Leaving access control management to a medical service opens up a risk of an accidental privilege misplacement or an intentional data leak. Being able to manage the access permissions only from trusted endpoints enables provable security for collaboration of such datasets.

You’ve reached the end of the document. Now what?

Get Hermes-core or read the scientific paper on Hermes

Contact us

Get whitepaper

Apply for the position

Our team will review your resume and provide feedback
within 5 business days

Thank you!
We’ve received your request and will respond soon.
Your resume has been sent!
Our team will review your resume and provide feedback
within 5 business days