Retroactive Interactive Key Sharing

Security for dynamic group communication in heterogeneous systems. Enabling end-to-end secure publish subscribe for end nodes with varying availability, communication methods and commitment. Encrypt now, decide recipient later.


This document provides documentation of the protocol RIKS as well as the implementation RiksKit available in Java and C++.


RIKS enables multicasting (1 sender to N receivers) of symmetrically encrypted messages while sharing the associated keys to a selected subset of the N receivers.

Key sharing is not restricted to occur before sending a message, i.e a key can be requested after receiving an encrypted message.

This effectively means that this protocol intends to make backward security flexible; a message recipient can retroactively be given access to priorly obtained or cached messages without re-encrypting them with a new key known to the recipient.


RIKS consists of two main parts:

  • Encrypting messages for multicasting using an arbitrary channel (e.g. MQTT)
  • Key Sharing using a secure and authenticated channel (e.g. HYKER PEP).

Use Cases

RIKS provides:

  • Sharing of symmetric keys
  • Encrypting messages using these keys
  • Tying cryptographic asymmetric identities to these keys and messages.

RIKS does not provide:

  • Distribution of asymmetric identities

RIKS requires:

  • A confidential authenticated and integrity protected one-to-one message channel
  • Externally supplied 2048-bit RSA keys (for authentication)

These dependencies of the RIKS protocol are provided in the complete RiksKit implementation.

Use Case: Chat App

Instant messaging app Ping Pal what to launch secure end-to-end encrypted messaging channels where teams could have highly confidential discussions. No-one outside the team, including Ping Pal should be able to access the messages.

Using existing data transport, CityChat choses to include RISK as a encryption layer for the new type of channel and thereby acquires desired properties.

New members can join the secure channel later and access the chat history without the burden on the senders to resend all the messages in the chat. The new member will receive the channel key automatically when joining. When a member leaves the channel the encryption key is revoked and the member can no longer access new chat messages.

The solution provides selective retroactive access to encrypted message history. All user keys are stored on user devices only, preventing any central access of the data.

Use Case: Car Pool

Bob owns a car service, Bob's Car Pool.
For security reasons, he has fitted each of his cars with a GPS system which constantly broadcasts the position of the car.
In order to not infringe on his customers privacy, he only wants access to the cars positions if the customers agree.

This way, Bob can ask a customer for the position of the car when something bad happens, for example if the car is stolen.
Alice however, will not have to give up her privacy when renting a car from Bob.
Further, she might be able to negotiate a deal whith Bob where she has no responsibility for a stolen car she rented if she gives up the position.

This way of encrypting is made possible through the retroactive key sharing in RIKS. We can let the car encrypt the positions and broadcast them in real time, but only give access to them later.


RIKS provides:

  • Confidentiality within the group of users which are given access to the latest key
  • Per user Access Control (trusted identities need to be supplied to the protocol)
  • Message Integrity
  • Non-Repudiation of messages
  • Message Replay Protection

RIKS does not provide:

  • Perfect Forward Secrecy
  • Message Ordering


RIKS defines a protocol for communicating securely in any number of 1 to N relationships.
The messages are distributed over an arbitrary channel and keying material is communicated over a secure, confidential, authenticated, and integrity protected one-to-one message channel.

The secure communication builds upon every node using a symmetric key for encrypting messages.
Every message sent will be marked with a key ID that can be used to identify the key that was used to encrypt the message.
These encryption keys can be shared with recipients of messages on request of recipients, i.e. keying material does not need to be shared before sending a message; keys can be shared afterwards.


Before a node can start sending encrypted messages, it needs to perform a rekey operation to obtain an encryption key.
This key is called the current key and is replaced for every rekeying operation.
Every new current key is assigned a new KEY_ID and a nonce counter initialised to zero.
Previous keys shall be saved in a cache in order to provide retroactive access.

Requesting a key

If a receiving node does not possess a key for a received message, it can request the key using the key id and sender uid provided in the message.
This is done by sending a ACCESS_REQ to the key holder.

Granting access to a key

On receiving an ACCESS_REQ, a sending node can reply with an ACCESS_RESP to the requesting node to grant access to all messages encrypted with the corresponding key.
No negative responses are given.

How to define access control is application specific and out of scope for RIKS.

Sending a message

A message can be protected with RIKS by encrypting the plaintext using the current key and placing the obtained ciphertext in a signed SYM_MSG and sending it over an arbitrary channel.

A receiving node can decrypt received messages by requesting the key from the sending node.
This key can be used to decrypt messages encrypted with it.
Keys obtained from other nodes should NEVER be used for encryption.

It is every sending node's responsibility to determine when rekeying occurs.

Cryptographic Functions

AES-GCM using:

  • 256 bit keys
  • 128 bit tags
  • 96 bit IVs

RSA-PSS using:

  • 2048 bit keys

RiksKit API

The RIKS implementation has the following API:

public RiksKit(CryptoBox cryptobox, RiksWhitelist whitelist)

Creates a new RiksKit using the identity in cryptobox and allowing key requests granted by the whitelist.

  • Parameters:
    • cryptobox — the cryptobox
    • whitelist — the whitelist
public String encryptMessage(Message message, String namespace) throws CryptoException, SymKeyExpiredException

Encrypts a map using the current encryption key.

  • Parameters:
    • message — the message to be protected
    • namespace — the namespace of the message
  • Returns: an encrypted representation of the map
public void encryptMessageAsync(Message message, String namespace, EncryptionCallback callback)

Encrypts a map using the current encryption key. Does not block.

  • Parameters:
    • message — the message to be protected
    • namespace — the namespace of the message
    • callback — the callback for returning the encrypted message
public Message decryptMessage(String message) throws Exception

Decrypts an encrypted version of the map using the current encryption key.

  • Parameters: message — the plaintext map
  • Returns: an encrypted representation of the map
  • Exceptions:
    • PublicKeyLookupException — if certificate could not be fetched from the KDS.
    • InterruptedException — if there was an interruption before the key could be fetched from the KDS.
    • SignatureException — if message contains an invalid signature
    • InvalidCipherTextException — if encrypted secret inside message has been manipulated, or the key is invalid
    • RiksMessageParseException — if the decrypted secret could not be correctly parsed
    • NonceOutsideReplayWindowException — if the nonce is outside the range of the replay window
    • ReplayException — if the message already has been received.
public <T extends DecryptionCallback & Serializable> void decryptMessageAsync(String message, T callback)

Decrypts an encrypted version of the map using the current encryption key. Does not block.

  • Parameters:
    • message — the plaintext map
    • callback — the callback for returning the decrypted message or exception
public void queryForKey(String uid, String keyid, int timeout) throws InterruptedException

Query for key. Blocks until key is in cache or timeout expired.

  • Parameters:
    • uid — the owner of the key
    • keyid — the key id
    • timeout — timeout in milliseconds
public void preShareKey(String recipientUid, String keyId) throws KeySharer.KeySharingException

Pre-share a key to a device.

  • Parameters:
    • recipientUid — the recipient of the key share
    • keyId — the key id
  • Exceptions: KeySharer.KeySharingException
public void rekey(String namespace)

Used to trigger an update of the current encryption key.

  • Parameters: namespace — the namespace to trigger the event in
public void resetReplayProtector(String namespace)

Resets the replay protector for the given namespace

  • Parameters: namespace — the namespace to reset the replay protector for.
public void resetReplayProtector()

Resets all replay protectors.

public Configuration getConfiguration()

Returns the configuration being used.

  • Returns: the configuration
@FunctionalInterface public interface EncryptionCallback

The callback used for returning the asynchronously encrypted/decrypted values.

@FunctionalInterface public interface DecryptionCallback

The callback used for returning the asynchronously encrypted/decrypted values.

results matching ""

    No results matching ""