PrivMX Public Key Infrastructure
PrivMX PKI are elements of PrivMX architecture that perform publication and verification of public keys for users and servers. They are important primarily in case of decentralized systems and enable, inter alia, verification of messages senders – checking whether a just received message contains the correct sender’s signature.
PrivMX does not use classical PKI solutions for this purpose which assume the use of external certification authorities. It also does not use PGP solutions based on mutual the signing of keys published on external servers.
The PrivMX Infrastructure of public keys is based on the ideas connected with CONIKS (https://coniks.cs.princeton.edu/) thanks to which, one of the assumptions of the PrivMX secure communication can be fulfilled – eliminating or minimizing the role of a "trusted third party" controlled by the external companies/organizations in respect of the application.
In case of decentralized applications, in the default configuration, each PrivMX server maintains its own base of public keys (keystores) of its users and makes it available through the API to client programs.
PrivMX keys bases are private databases publishing hash (SHA256) of its entire content and history of changes of the hash using the append-only structure (i.e. blockchain). Thanks to this, these databases can be subject to audit and monitoring for consistency conducted by other PrivMX servers. Each retrieved public key (keystore) is supplied with a proof of correctness that is associated with the hash of the entire database. Verification of correctness of the downloaded keystore consists of two steps – checking the correctness of database hash based on its history (also performed together with other servers – audits, web-of-trust) and verification of the provided proof of keystore correctness.
The following subchapters describe in more detail the features and performance of PrivMX PKI and the default way to use these mechanisms in a decentralized application.
Private key base with public history of changes
The PrivMX architecture assumes the handling of two types of keystores:
- pgpkeystore – a PGP-compatible keystore containing a full history of changes. It allows to perform operations such as the annulment of keys (revoke) and the adding of subkeys (and other PGP packages i.e. public key, signature, userid). Keystore changes of this type can be made by the holder of the distinguished private key.
- simplekeystore – a simple structure keystore that contains a list of public keys. Such a keystore can be modified by owners of any matching private keys. The history of keystore changes of this type are not saved in it.
Keystores, in addition to key data and information on their type, can also contain attachments of any files.
Standard server and client implementations of PrivMX provide the appropriate functions for using both types of keystores. They allow to create, read and modify them using private keys. This document omits the description of these functions.
The set (database) of keystores stored by the PrivMX server has the structure of the binary Merkle tree. In simple terms it can be presented as such that the tree leaves are keystores and the nodes other than leaves consist of the SHA256 hashes of their descendants. The tree root hash is therefore the hash of the entire database of keystores – it is often called "tree revision" or "base revision".
Every change in the tree (i.e., adding or modifying of the keystore) causes changes to the corresponding tree branches and changes the hash of the entire base. In addition to the history of tree changes, PrivMX PKI also stores the history of changes of hash of the entire base in the form of the append-only list; each element of this history contains subsequent base hash and hash of the previous element in the history.
This list, similar to the blockchain structure, is publicly made available by the PrivMX server (
API.pkiGetHistory). It does not contain any data (keys and attachments) in itself that are stored by PrivMX PKI, but it is sufficient to verify the keys retrieved from the given server and to perform external base audits.
Downloading and verifying public keys
Access to keystores at the server API level is achieved through the use of readable alphanumeric identifiers such as "server:simplito.com", "user:john#example.com". These identifiers are transformed by the server into zero-one paths in a binary tree using a verifiable random function (VRF). PrivMX PKI uses 256-bit paths-identifiers and compresses them in order to avoid the creation of trees that are too deep.
The parameters of the
API.pkiKeyStoreGet function are:
- name – alphanumeric identifier containing a colon (it designates namespaces);
- IncludeAttachments – if equal true (1), the function, in addition to key data, also returns all attachments of the selected keystore;
- revision – revision (hash) of the database from which the keystore has to be retrieved.
The function returns keystore data (if the corresponding keystore exists in the database), correctness proof, and attachments, if required. "Correctness proof" is the VRF-function-generated string containing the corresponding binary path fragments and all hashes of Merkle tree nodes (including root) needed for the client program to verify that the downloaded keystore belongs to the base of the required revision, and that there is no missing data in it. Appropriate functions for this verification are provided in the standard PrivMX client implementations.
Omitting the revision parameter when retrieving the keystore causes that the server will use the revision of the database at the time of the query. Then the verifying of the correctness requires additional retrieval of the database history in order to find in it the database hash-revision contained in the proof.
Downloading the base history and keystores is possible after establishing the "standard" PrivMX TLS connection – it is available by default for all client programs. The server also provides functions
API.pkiKeyStoreDelete the access to which, is limited and configured depending on the specific application.
Audits, consensus, web-of-trust
Despite the possibility of the verification described above, the client program must "believe" the server from which it downloads the keys. In the situation of decentralized and open application it is not an obvious issue when many servers belonging to different people and organizations are used. In order to avoid situations where one client is presented a history and keys other than those which are seen by the rest of the network – it is possible to check the server by performing an audit.
The audit consists in sending to selected (other) servers the requests for verification of the history of the database of the server being inspected. This involves establishing a connection and calling
API.pkiConfirmRevision in selected servers giving:
- hostname – domain name of the server we want to check;
- revision – hash of the database the existence of which on the target server we want to confirm.
Servers that are asked for the audit check if the hostname server provides them with a consistent history containing the given revision — they make the appropriate calls of
API.pkiGetHistory and use the previously saved hostname server database history. Each of the servers that are asked for help finally return information whether they also see the same revision on the hostname server or not.
After collecting this information, the client program can inform the user accordingly about the level of credibility of the server. If all answers from the auditors are positive or all are negative, then we can talk about reaching an agreement (consensus) about the reliability of the server being inspected. All intermediate situations where the responses are different or when some servers are not responding, need configuration and interface in the client application. Appropriate heuristics can be used to "calculate the level of the consensus" or special operations can be made available to the user — marking the server as untrusted, renewed attempt of verification, consultations with other servers users, etc.
The number and selection of server auditors are two of the most important issues when outsourcing audits. The application that performs them can use the established, trusted PrivMX servers and/or may allow users to build their own web-of-trust – networks of trusted servers. It can choose auditors at random or according to any of their own criteria. The PrivMX architecture does not determine the manner of selecting servers, it depends on the specific application.
Examples of using PrivMX PKI
Below we describe the actions taken by default by the PrivMX Server configured to work within the framework of a decentralized application (for example, the application PrivMX WebMail, available at the address https://privmx.com).
- During PrivMX server installation, the server’s private key is set, and the corresponding public key is placed in the PrivMX PKI under the identifier "server".
- Every call of the
API.registerregistering a new user inserts the public key of the user (IdentityKeyPub) into the database with the identifier "user:username”. This key is accompanied by attachments that include the user’s avatar, the SID of their public mailbox and other public profile data. This keystore is used by other users to verify messages signed by the user username#hostname.
- The first user creating an account on a new server obtains administrator rights. In the course of the first login of this user, new keystore "admin" is created (of the type of simplekeystore) which stores public keys of all administrators of the server. This keystore is made available to all users of the server who can then check whether, for example, a given message comes from the server’s administrator.
The mentioned PrivMX WebMail application also allows server administrators to create their own webs-of-trust. Admins send special invitation messages to other admins, and the acceptance of such invitations causes that both sides add each other to their private lists of trusted servers. These lists are stored as attachments in the "admin:hostname" keystore and they are read by each user during each login. In this way the administrator publishes (suggests) lists of trusted servers to his users/clients. In addition, the operation of the
API.pkiRevisionConfirm function is limited on the server so that it only performs an audit when it is requested by the server from the list of trusted servers.