RIKS
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.
Overview
This document provides documentation of the protocol RIKS
as well as the implementation RiksKit
available in Java
and C++
.
Purpose
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.
Structure
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 completeRiksKit
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.
Properties
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
Usage
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.
Rekeying
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 cryptoboxwhitelist
— 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 protectednamespace
— 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 protectednamespace
— the namespace of the messagecallback
— 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 signatureInvalidCipherTextException
— if encrypted secret inside message has been manipulated, or the key is invalidRiksMessageParseException
— if the decrypted secret could not be correctly parsedNonceOutsideReplayWindowException
— if the nonce is outside the range of the replay windowReplayException
— 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 mapcallback
— 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 keykeyid
— the key idtimeout
— 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 sharekeyId
— 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.