PrivMX Architecture – client

Preliminary information

Client module is the central part of systems based on PrivMX architecture. The server offers limited functionality and by default the client application implements the entirety of the business logic of the system simultaneously ensuring proper data encryption.

The standard PrivMX client library provides basic components for building system logic based on:

  • mechanisms for secure account creation and login;
  • "high-level" objects such as directories, files, inboxes, outboxes etc;
  • sharing of data;
  • encryption of data before sending to the server, coupled with proper generation and storage of keys.

In this section we describe briefly the above functions.

Extended ECC keys

The standard PrivMX client module uses the so-called extended ECC keys (see BIP 32 specification) – these are ordinary 256bit privkey keys and pubkey 512bit (packaged to 257bit) expanded with an additional data element, so called chaincode with a length of 256 bit – "additional entropy source". The extended keys are written in this document with a plus sign – as privkey+chaincode and pubkey+chaincode.

Chaincodes are used by the PrivMX client in the process of derivation of new keys from existing keys (CKD function with BIP 32) and as keys to encrypt user data with symmetric algorithm (AES).

Creating accounts

The PrivMX TLS protocol allows for the client application to log in, i.e. to force conducting an additional handshake procedure based on SRP. To enable this, the server must be in possession of the SRP verifier. Ensuring this is one of the goals of the account creation procedure. Another goal of this procedure is to set and save the private and public key for the new user.

New users’ accounts on the PrivMX server can be created by default by the client programs only on the basis of "invitations" – 32-byte random tokens. To generate tokens, the API.generateNewUserToken method is used, which is available in the logged-in connection for distinguished users ("administrators"). The first token for the first user is usually generated during the PrivMX server installation procedure.

Any connected client program can start the account creation procedure, if it has a valid token and has already asked the user to choose their name and password.

  1. The client program randomly generates Salt (16 bytes) and selects NumberOfRounds (number from 4000-5000);
  2. Determines MixedPassword = H( user password, Salt, NumberOfRounds ), where H is a hashing algorithm set by the client program, PBKDF2-SHA512 for example;
  3. Calculates the srpVerifier using hash value = SHA512( MixedPassword ) % 16 bytes;
  4. Randomly generates MasterKey = extended private ECC key;
  5. Sets privData = AES256Encrypt( MasterKey ) with password equal to SHA256( MixedPassword );
  6. Calculates identityKey = CKD( MasterKey, m/0′ ), then identityKeyPub = extended public key counted for identityKey;
  7. Calls the API.register method to which it conveys:
    • the token received from the administrator;
    • new user name;
    • srpVerifier – will be used by the server during subsequent logins;
    • Salt, NumberOfRounds and hashing algorithm name – the server will provide this data to each client who will want to log in to that account;
    • privData, which will be safely downloadable for the client after login (see below);
    • identityKeyPub – a new user’s public key that will be published by the server within the PrivMX PKI service;
    • signature – signature of the set of all the above data made with private identityKey.

The server saves the above data and uses them in subsequent attempts to log in to the newly created account.

Login and initialization of the client

A client application that uses the PrivMX module usually starts its operation with establishing a standard connection to the PrivMX server. For this purpose, it connects with a fixed API endpoint or at first uses the Service Discovery procedure for the specified hostname or user#hostname address.

If the application wants to obtain access to a particular user’s data or wants to send a message on his behalf – it must perform the login procedure. Assuming that the client has already established the PrivMX TLS standard connection and has the user’s ID and password provided by the user:

  1. The client retrieves from the server the Salt and the NumberOfRounds saved for a given username (API.getLoginParams);
  2. Using these data and the password of the user, it counts MixedPassword and srpVerifier – in the same way as during the creation of the account;
  3. Asks, by calling the appropriate PrivMX TLS function, to perform the SRP handshake, as a result of which, the encryption keys for the connection are changed and the client obtains authorization as a user possessing the account, obtains access to the API methods for the logged users;
  4. Retrieves from the server the user’s privData (API.getPrivData);
  5. Calculates MasterKey = AES256Decrypt( privData ) with password equal to SHA256( MixedPassword );
  6. Calculates identityKey and identityKeyPub – based on MasterKey, in the same way as during the setting up of the account.

The two keys obtained in result of performing the above procedure are the basis for the further operation of the standard client module:

  • The extended private MasterKey is for the client program the "master handle" for user data stored on the server. Two additional keys are generated from it:
    • extended private key HomeDir = CKD( MasterKey, m/1′ ) designating and giving full access to the "home directory" of the user;
    • extended private key SinkList = CKD( MasterKey, m/2′ ) designating and giving full access to the file storing the private keys to the user mailboxes.
  • extended private IdentityKey = CKD( MasterKey, m/0′ ) is the user’s private key which is used to decrypt messages addressed to the user and to confirm their identity (signatures) etc.

All of the above mentioned keys are generated by the client program and they are not stored on the server side.

After the first login, the standard client module cannot read the HomeDir directory and the SinkList file because they do not exist yet. They must be created and may be filled with initial data depending on the application. The default action is to create a blank home directory and the first mailbox which private key is placed in the SinkList file. The public key of the mailbox can be set as the default public SID of the user associated with address name#hostname.

Files and directories

The PrivMX Server does not offer API functions related to objects such as files or directories. If the client application wants to use such objects and store them on the PrivMX server, then it must simulate them using the available API methods. The standard client module does this by making the following assumptions:

  • file is a descriptor with associated blocks. The file data is stored in blocks, and the file metadata in the descriptor’s Extra field.
  • Directory is a special file (in json format) that contains a list of names and keys of files and subdirectories that are to be located in a given directory. Descriptor’s Extra field contains the directory metadata.

The HomeDirKey key obtained after login is an extended private key of the main directory. Blocks of that descriptor contain the list of all files and directories of the "root level" – their identifiers, public and private keys. In this way, the PrivMX client obtains the tree structure of the encrypted files and directories.

This structure and content of files are hidden from the server because the standard PrivMX client module encrypts the data which it places in the blocks and in the Extra descriptor fields. It uses symmetric encryption for that purpose (AES256) with chaincodes from extended ECC keys as passwords.

For example: in order to save a file in directory k, the client program must have an extended private key of this directory (kpriv+kchaincode) to prove that it has write/modify access rights. It then performs the procedure of creating a new descriptor with appropriately encrypted data and appending it to directory k:

  1. Randomly creates a 256-bit KS key to encrypt the file data.
  2. Divides the file into blocks, encrypts each of them with the KS key and assigns BID (hash of cryptogram) to them.
  3. Randomly creates a new extended private key (dpriv+dchaincode) and generates an extended public key (dpub+dchaincode) from it – the new file identifier.
  4. Prepares the Metadata structure that includes:
    • type = "file"
    • data = { filename, mimetype, size, date of creation and modification of the file }
    • blockskey = KS
  5. Creates a new descriptor on the server, sends prepared blocks, dpub key, proper DID, and:
    • Extra = AES256Encrypt( data = Metadata, password = dchaincode )
    • Signature of the entire request made with the dpriv key
  6. Using the private kpriv key, modifies the directory/descriptor k so that it adds to its data (json) a line containing:
    • name = filename
    • type = "file"
    • pub = (dpub+dchaincode)
    • encpriv = AES256Encrypt( data = dpriv, password = kpriv )

The possibility of reading the data connected with the file/directory descriptor depends on the keys that the client program possesses:

  • Possessing of only DID does not give access to decrypted data. It allows to check whether a given descriptor exists, and thanks to access to the Blocks field, allows the downloading of blocks of encrypted data.
  • Possessing of extended public key (dpub+dchaincode) allows the above and additionally decrypts the Extra field, that is, obtaining the file metadata and the "blockskey" key. Thanks to the latter one the client can decrypt the data blocks – they will then receive the decrypted file data. In case of a directory the client will obtain the list of the files and subdirectories that are located in it and thereby the possibility to read the directory "inwards".
  • Possessing of an extended private key (dpriv+dchaincode) allows to read all the data as above and additionally enables the client program to modify the descriptor and remove it. In case it is a directory – it is possible to decrypt the private keys of files and subdirectories and consequently to modify and delete them.

Sending and receiving messages

After conducting the login procedure, the standard PrivMX client library obtains the SinkList key to the file containing the user’s mailboxes list. For each box it is possible to find here the name, description and extended private key. This file is updated accordingly after creating a new mailbox or removing an unwanted one.

The client program must know the recipient’s mailbox identifier (SID) in order to send the message to the recipient. Distribution of SIDs can be implemented in various ways. By default a decentralized system configuration is assumed and therefore the standard client module creates the first mailbox right after the first login, and adds it to the SinkList and public user’s record in the PrivMX PKI service. Thanks to PrivMX PKI, message senders can retrieve (and verify) the default SID of the user. More on this topic can be read in the last chapter of the document.

The standard client module uses API.messagePut in order to leave its message in the right mailbox on the target server. However, it uses message blocks and the Extra field in a specific way:

  • data blocks are mainly used for transferring files – attachments to messages. For each attachment, a new key is generated and used to encrypt blocks of this attachment (AES256Encrypt).
  • The main structure of the message is placed in the Extra field and is encrypted with a common secret generated according to the ECIES scheme for the sender’s IdentityKey private key and the SID public key of the mailbox. The standard structure of the message includes, among others:
    • title and contents of the message – text fields formatted freely by the client;
    • senderName – sender’s name (e.g. "John Smith")
    • attachments – list of attachments; for each one there are specified:
      • name and mimetype;
      • blocks – BIDs of this attachement’s blocks;
      • key – the key encrypting these blocks.

The use of the public key of the mailbox (SID) in the ECIES schema causes that the content of the transmitted data will be able to be retrieved, decrypted and read only by the private key holder of the target mailbox. If this is for example the default box of the user who has not shared any of their keys with anybody (which is the most common case), the message will be read only by that user.

Data sharing

The standard way to share data within the framework of the PrivMX architecture consists in distributing appropriate keys to specific objects stored by the server. For example, if you want to share a read-only file with another user, you must give them a public key to it.

Public keys are calculated from private keys, so for example, by distributing a private key to the descriptor, we give the recipient the possibility to modify and also to read it. In the case of mailboxes, obtaining the right to read (private key) also gives the right to put new messages in it.

The way of distributing the keys depends entirely on the client application, its purpose, way of operation and the user’s interface solutions. Keys to common objects (most often to files and directories) are most frequently sent through messages to default or special users’ mailboxes. Such an approach also enables data sharing between users from different servers, although the condition is that the configuration of servers should allow access to data also for "foreign users" also.

Notes on client implementation and modification

All the keys used by the PrivMX architecture based system are randomly generated by the client module, therefore one of the most important elements affecting the security of such a system is the high quality of the random number generator. Each implementation of the PrivmX client module should take care of this.

Account setup and login procedures can be extended by using the 2-factor authentication scheme.

Grouping of requests is done by PrivMX TLS protocol optimization, that improves conducting operations on files and directories that require making multiple API queries. Of course, such optimization also requires server-side support.

Derived by default, after login, the SinkList key to the file containing the list of private mailboxes can be replaced with a regular file in the HomeDir directory.

It is also possible to store the private keys of mailboxes inside themselves – in their Extra field. This can be a useful optimization when the server API is extended with functions that can list all user’s mailboxes. The private keys of mailboxes should then be encrypted using the chaincode of the HomeDir key as a password (for example).

By default, through the procedure of CKD (keys derivation) only the "master mountpoints" are obtained for the user, but the client application can create for own needs any larger hierarchies of private keys.

Client programs can create messages that can be decrypted only by specific users (and not by any owner of the private key of the mailbox). For this purpose, in the content of the message there may be included a portion encrypted with ECIES scheme using the sender’s private key and the public key of the recipient (and not the mailbox).

Server configuration parameters that are relevant to client programs (e.g. maximum data block size) are available for reading by the API.getServerConfig method.


Next: PrivMX PKI – private database of keys with public history of changes, downloading and verifying public keys, audits, consensus, web-of-trust, example of using PrivMX PKI.
TOC: PrivMX Architecture
PDF version: PrivMX Architecture – whitepaper (pdf)