Skip to main content

VAC-DECENTRALIZED-MESSAGING-ETHEREUM

  • Status: raw
  • Category: informational
  • Editor: Ramses Fernandez-Valencia <ramses@status.im>

Abstract

This document introduces a decentralized group messaging protocol using Ethereum adresses as identifiers. It is based in the proposal DCGKA by Weidner et al. It includes also approximations to overcome limitations related to using PKI and the multi-device setting.

Motivation

The need for secure communications has become paramount. Traditional centralized messaging protocols are susceptible to various security threats, including unauthorized access, data breaches, and single points of failure. Therefore a decentralized approach to secure communication becomes increasingly relevant, offering a robust solution to address these challenges.

Secure messaging protocols used should have the following key features:

  1. Asynchronous Messaging: Users can send messages even if the recipients are not online at the moment.

  2. Resilience to Compromise: If a user's security is compromised, the protocol ensures that previous messages remain secure through forward secrecy (FS). This means that messages sent before the compromise cannot be decrypted by adversaries. Additionally, the protocol maintains post-compromise security (PCS) by regularly updating keys, making it difficult for adversaries to decrypt future communication.

  3. Dynamic Group Management: Users can easily add or remove group members at any time, reflecting the flexible nature of communication within the app.

In this field, there exists a trilemma, similar to what one observes in blockchain, involving three key aspects:

  1. security,
  2. scalability, and
  3. decentralization.

For instance, protocols like the MLS perform well in terms of scalability and security. However, they falls short in decentralization.

Newer studies such as CoCoa improve features related to security and scalability, but they still rely on servers, which may not be fully trusted though they are necessary.

On the other hand, older studies like Causal TreeKEM exhibit decent scalability (logarithmic) but lack forward secrecy and have weak post-compromise security (PCS).

The creators of DCGKA introduce a decentralized, asynchronous secure group messaging protocol that supports dynamic groups. This protocol operates effectively on various underlying networks without strict requirements on message ordering or latency. It can be implemented in peer-to-peer or anonymity networks, accommodating network partitions, high latency links, and disconnected operation seamlessly. Notably, the protocol doesn't rely on servers or a consensus protocol for its functionality.

This proposal provides end-to-end encryption with forward secrecy and post-compromise security, even when multiple users concurrently modify the group state.

Theory

Protocol overview

This protocol makes use of ratchets to provide FS by encrypting each message with a different key.

In the figure one can see the ratchet for encrypting a sequence of messages. The sender requires an initial update secret I_1, which is introduced in a PRG. The PRG will produce two outputs, namely a symmetric key for AEAD encryption, and a seed for the next ratchet state. The associated data needed in the AEAD encryption includes the message index i. The ciphertext c_i associated to message m_i is then broadcasted to all group members. The next step requires deleting I_1, k_i and any old ratchet state.

After a period of time the sender may replace the ratchet state with new update secrets I_2, I_3, and so on.

To start a post-compromise security update, a user creates a new random value known as a seed secret and shares it with every other group member through a secure two-party channel. Upon receiving the seed secret, each group member uses it to calculate an update secret for both the sender's ratchet and their own. Additionally, the recipient sends an unencrypted acknowledgment to the group confirming the update. Every member who receives the acknowledgment updates not only the ratchet for the original sender but also the ratchet for the sender of the acknowledgment. Consequently, after sharing the seed secret through n - 1 two-party messages and confirming it with n - 1 broadcast acknowledgments, every group member has derived an update secret and updated their ratchet accordingly.

When removing a group member, the user who initiates the removal conducts a post-compromise security update by sending the update secret to all group members except the one being removed. To add a new group member, each existing group member shares the necessary state with the new user, enabling them to derive their future update secrets.

Since group members may receive messages in various orders, it's important to ensure that each sender's ratchet is updated consistently with the same sequence of update secrets at each group member.

The network protocol used in this scheme ensures that messages from the same sender are processed in the order they were sent.

Components of the protocol

This protocol relies in 3 components: authenticated causal broadcast (ACB), decentralized group membership (DGM) and 2-party secure messaging (2SM).

Authenticated causal broadcast

A causal order is a partial order relation < on messages. Two messages m_1 and m_2 are causally ordered, or m_1 causally precedes m_2 (denoted by m_1 < m_2), if one of the following contiditions hold:

  1. m_1 and m_2 were sent by the same group member, and m_1 was sent before m_2.
  2. m_2 was sent by a group member U, and m_1 was received and processed by U before sending m_2.
  3. There exists m_3 such that m_1 < m_3 and m_3 < m_2.

Causal broadcast requires that before processing m, a group member must process all preceding messages {m' | m' < m}.

The causal broadcast module used in this protocol authenticates the sender of each message, as well as its causal ordering metadata, using a digital signature under the sender’s identity key. This prevents a passive adversary from impersonating users or affecting causally ordered delivery.

Decentralized group membership

This protocol assumes the existence of a decentralized group membership function (denoted as DGM) that takes a set of membership change messages and their causal order relantionships, and returns the current set of group members’ IDs. It needs to be deterministic and depend only on causal order, and not exact order.

2-party secure messaging (2SM)

This protocol makes use of bidirectional 2-party secure messaging schemes, which consist of 3 algorithms: 2SM-Init, 2SM-Send and 2SM-Receive.

Function 2SM-Init

This function takes two IDs as inputs: ID1 representing the local user and ID2 representing the other party. It returns an initial protocol state sigma. The 2SM protocol relies on a Public Key Infrastructure (PKI) or a key server to map these IDs to their corresponding public keys. In practice, the PKI should incorporate ephemeral prekeys. This allows users to send messages to a new group member, even if that member is currently offline.

Function 2SM-Send

This function takes a state sigma and a plaintext m as inputs, and returns a new state sigma’ and a ciphertext c.

Function 2SM-Receive

This function takes a state sigma and a ciphertext c, and returns a new state sigma’ and a plaintext m.

This function takes a state sigma and a ciphertext c, and returns a new state sigma’ and a plaintext m.

Function 2SM Syntax

The variable sigma denotes the state consisting in the variables below:

sigma.mySks[0] = sk
sigma.nextIndex = 1
sigma.receivedSk = empty_string
sigma.otherPk = pk`<br/>
sigma.otherPksender = “other”
sigma.otherPkIndex = 0

2SM-Init

On input a key pair (sk, pk), this functions otuputs a state sigma.

2SM-Send

This function encrypts the message m using sigma.otherPk, which represents the other party’s current public key. This key is determined based on the last public key generated for the other party or the last public key received from the other party, whichever is more recent. sigma.otherPkSender is set to me in the former case and other in the latter case.

Metadata including otherPkSender and otherPkIndex are included in the message to indicate which of the recipient’s public keys is being utilized.

Additionally, this function generates a new key pair for the local user, storing the secret key in sigma.mySks and sending the public key. Similarly, it generates a new key pair for the other party, sending the secret key (encrypted) and storing the public key in sigma.otherPk.

sigma.mySks[sigma.nextIndex], myNewPk) = PKE-Gen()
(otherNewSk, otherNewPk) = PKE-Gen()
plaintext = (m, otherNewSk, sigma`.nextIndex, myNewPk)
msg = (PKE-Enc(sigma.otherPk, plaintext), sigma.otherPkSender, sigma.otherPkIndex)
sigma.nextIndex++
(sigma.otherPk, sigma.otherPkSender, sigma.otherPkIndex) = (otherNewPk, "me", empty_string)
return (sigma`, msg)

2SM-Receive

This function utilizes the metadata of the message c to determine which secret key to utilize for decryption, assigning it to sk. If the secret key corresponds to one generated by ourselves, that secret key along with all keys with lower index are deleted. This deletion is indicated by sigma.mySks[≤ keyIndex] = empty_string. Subsequently, the new public and secret keys contained in the message are stored.

(ciphertext, keySender, keyIndex) = c
if keySender = "other" then
sk = sigma.mySks[keyIndex]
sigma.mySks[≤ keyIndex] = empty_string
else sk = sigma.receivedSk
(m, sigma.receivedSk, sigma.otherPkIndex, sigma.otherPk) = PKE-Dec(sk, ciphertext)
sigma.otherPkSender = "other"
return (sigma, m)

PKE Syntax

The required PKE that MUST be used is ElGamal with a 2048-bit modulus p.

Parameters

The following parameters must be used:

p = 308920927247127345254346920820166145569
g = 2

PKE-KGen

Each user u MUST do the following:

PKE-KGen():
a = randint(2, p-2)
pk = (p, g, g^a)
sk = a
return (pk, sk)

PKE-Enc

A user v encrypting a message m for u MUST follow these steps:

PKE-Enc(pk):
k = randint(2, p-2)
eta = g^k % p
delta = m * (g^a)^k % p
return ((eta, delta))

PKE-Dec

The user u recovers a message m from a ciphertext c by performing the following operations:

PKE-Dec(sk):
mu = eta^(p-1-sk) % p
return ((mu * delta) % p)

DCGKA Syntax

Auxiliary functions

There exist 6 functions that are auxiliary for the rest of components of the protocol, namely:

init

This function takes an ID as input and returns its associated initial state, denoted by gamma:

gamma.myId = ID
gamma.mySeq = 0
gamma.history = empty
gamma.nextSeed = empty_string
gamma.2sm[·] = empty_string
gamma.memberSecret[·, ·, ·] = empty_string
gamma.ratchet[·] = empty_string
return (gamma)

encrypt-to

Upon reception of the recipient’s ID and a plaintext, it encrypts a direct message for another group member. Should it be the first message for a particular ID, then the 2SM protocol state is initialized and stored in gamma.2sm[recipient.ID]. One then uses 2SM_Send to encrypt the message and store the updated protocol in gamma.

if gamma.2sm[recipient_ID] = empty_string then
gamma.2sm[recipient_ID] = 2SM_Init(gamma.myID, recipient_ID)
(gamma.2sm[recipient_ID], ciphertext) = 2SM_Send(gamma.2sm[recipient_ID], plaintext)
return (gamma, ciphertext)

decrypt-from

After receiving the sender’s ID and a ciphertext, it behaves as the reverse function of encrypt-to and has a similar initialization:

if gamma.2sm[sender_ID] = empty_string then
gamma.2sm[sender_ID] = 2SM_Init(gamma.myID, sender_ID)
(gamma.2sm[sender_ID], plaintext) = 2SM_Receive(gamma.2sm[sender_ID], ciphertext)
return (gamma, plaintext)

update-ratchet

This function generates the next update secret I_update for the group member ID. The ratchet state is stored in gamma.ratchet[ID]. It is required to use a HMAC-based key derivation function HKDF to combine the ratchet state with an input, returning an update secret and a new ratchet state.

(updateSecret, gamma.ratchet[ID]) = HKDF(gamma.ratchet[ID], input)
return (gamma, updateSecret)

member-view

This function calculates the set of group members based on the most recent control message sent by the specified user ID. It filters the group membership operations to include only those observed by the specified ID, and then invokes the DGM function to generate the group membership.

ops = {m in gamma.history st. m was sent or acknowledged by ID}
return DGM(ops)

generate-seed

This functions generates a random bit string and sends it encrypted to each member of the group using the 2SM mechanism. It returns the updated protocol state and the set of direct messages (denoted as dmsgs) to send.

gamma.nextSeed = random.randbytes()
dmsgs = empty
for each ID in recipients:
(gamma, msg) = encrypt-to(gamma, ID, gamma.nextSeed)
dmsgs = dmsgs + (ID, msg)
return (gamma, dmsgs)

Creation of a group

A group is generated in a 3 steps procedure:

  1. A user calls the create function and broadcasts a control message of type create.
  2. Each receiver of the message processes the message and broadcasts an ack control message.
  3. Each member processes the ack message received.

create

This function generates a create control message and calls generate-seed to define the set of direct messages that need to be sent. Then it calls process-create to process the control message for this user. The function process-create returns a tuple including an updated state gamma and an update secret I.

control = (“create”, gamma.mySeq, IDs)
(gamma, dmsgs) = generate-seed(gamma, IDs)
(gamma, _, _, I, _) = process-create(gamma, gamma.myId, gamma.mySeq, IDs, empty_string)
return (gamma, control, dmsgs, I)

process-seed

This function initially employs member-view to identify the users who were part of the group when the control message was dispatched. Then, it attempts to acquire the seed secret through the following steps:

  1. If the control message was dispatched by the local user, it uses the most recent invocation of generate-seed stored the seed secret in gamma.nextSeed.
  2. If the control message was dispatched by another user, and the local user is among its recipients, the function utilizes decrypt-from to decrypt the direct message that includes the seed secret.
  3. Otherwise, it returns an ack message without deriving an update secret.

Afterwards, process-seed generates separate member secrets for each group member from the seed secret by combining the seed secret and each user ID using HKDF. The secret for the sender of the message is stored in senderSecret, while those for the other group members are stored in gamma.memberSecret. The sender's member secret is immediately utilized to update their KDF ratchet and compute their update secret I_sender using update-ratchet. If the local user is the sender of the control message, the process is completed, and the update secret is returned. However, if the seed secret is received from another user, an ack control message is constructed for broadcast, including the sender ID and sequence number of the message being acknowledged.

The final step computes an update secret I_me for the local user invoking the process-ack function.

recipients = member-view(gamma, sender) - {sender}
if sender = gamma.myId then seed = gamma.nextSeed; gamma.nextSeed =
empty_string
else if gamma.myId in recipients then (gamma, seed) = decrypt-from(gamma,
sender, dmsg)
else
return (gamma, (ack, ++gamma.mySeq, (sender, seq)), empty_string ,
empty_string , empty_string)

for ID in recipients do gamma.memberSecret[sender, seq, ID] = HKDF(seed, ID)
senderSecret = HKDF(seed, sender)
(gamma, I_sender) = update-ratchet(gamma, sender, senderSecret)
if sender = gamma.myId then return (gamma, empty_string , empty_string ,
I_sender, empty_string)
control = (ack, ++gamma.mySeq, (sender, seq))
members = member-view(gamma, gamma.myId)
forward = empty
for ID in {members - (recipients + {sender})}
s = gamma.memberSecret[sender, seq, gamma.myId]
(gamma, msg) = encrypt-to(gamma, ID, s)
forward = forward + {(ID, msg)}
(gamma, _, _, I_me, _) = process-ack(gamma, gamma.myId, gamma.mySeq,
(sender, seq), empty_string)
return (gamma, control, forward, I_sender, I_me)

process-create

This function is called by the sender and each of the receivers of the create control message. First, it records the information from the create message in the gamma.history+ {op}, which is used to track group membership changes. Then, it proceeds to call process-seed.

op = (”create”, sender, seq, IDs)
gamma.history = gamma.history + {op}
return (process-seed(gamma, sender, seq, dmsg))

process-ack

This function is called by those group members once they receive an ack message. In process-ack, ackID and ackSeq are the sender and sequence number of the acknowledged message. Firstly, if the acknowledged message is a group membership operation, it records the acknowledgement in gamma.history.

Following this, the function retrieves the relevant member secret from gamma.memberSecret, which was previously obtained from the seed secret contained in the acknowledged message.

Finally, it updates the ratchet for the sender of the ack and returns the resulting update secret.

if (ackID, ackSeq) was a create / add / remove then
op = ("ack", sender, seq, ackID, ackSeq)
gamma.history = gamma.history + {op}`
s = gamma.memberSecret[ackID, ackSeq, sender]
gamma.memberSecret[ackID, ackSeq, sender] = empty_string
if (s = empty_string) & (dmsg = empty_string) then return (gamma, empty_string,
empty_string, empty_string, empty_string)
if (s = empty_string) then (gamma, s) = decrypt-from(gamma, sender, dmsg)
(gamma, I) = update-ratchet(gamma, sender, s)
return (gamma, empty_string, empty_string, I, empty_string)

The HKDF function MUST follow RFC 5869 using the hash function SHA256.

Post-compromise security updates and group member removal

The functions update and remove share similarities with create: they both call the function generate-seed to encrypt a new seed secret for each group member. The distinction lies in the determination of the group members using member view. In the case of remove, the user being removed is excluded from the recipients of the seed secret. Additionally, the control message they construct is designated with type update or remove respectively.

Likewise, process-update and process-remove are akin to process-create. The function process-update skips the update of gamma.history, whereas process-remove includes a removal operation in the history.

update

control = ("update", ++gamma.mySeq, empty_string)
recipients = member-view(gamma, gamma.myId) - {gamma.myId}
(gamma, dmsgs) = generate-seed(gamma, recipients)
(gamma, _, _, I , _) = process-update(gamma, gamma.myId, gamma.mySeq,
empty_string, empty_string)
return (gamma, control, dmsgs, I)

remove

control = ("remove", ++gamma.mySeq, empty)
recipients = member-view(gamma, gamma.myId) - {ID, gamma.myId}
(gamma, dmsgs) = generate-seed(gamma, recipients)
(gamma, _, _, I , _) = process-update(gamma, gamma.myId, gamma.mySeq, ID,
empty_string)
return (gamma, control, dmsgs, I)

process-update

return process-seed(gamma, sender, seq, dmsg)

process-remove

op = ("remove", sender, seq, removed)
gamma.history = gamma.history + {op}
return process-seed(gamma, sender, seq, dmsg)

Group member addition

add

When adding a new group member, an existing member initiates the process by invoking the add function and providing the ID of the user to be added. This function prepares a control message marked as add for broadcast to the group. Simultaneously, it creates a welcome message intended for the new member as a direct message. This welcome message includes the current state of the sender's KDF ratchet, encrypted using 2SM, along with the history of group membership operations conducted so far.

control = ("add", ++gamma.mySeq, ID)
(gamma, c) = encrypt-to(gamma, ID, gamma.ratchet[gamma.myId])
op = ("add", gamma.myId, gamma.mySeq, ID)
welcome = (gamma.history + {op}, c)
(gamma, _, _, I, _) = process-add(gamma, gamma.myId, gamma.mySeq, ID, empty_string)
return (gamma, control, (ID, welcome), I)

process-add

This function is invoked by both the sender and each recipient of an add message, which includes the new group member. If the local user is the newly added member, the function proceeds to call process-welcome and then exits. Otherwise, it extends gamma.history with the add operation.

Line 5 determines whether the local user was already a group member at the time the add message was sent; this condition is typically true but may be false if multiple users were added concurrently.

On lines 6 to 8, the ratchet for the sender of the add message is updated twice. In both calls to update-ratchet, a constant string is used as the ratchet input instead of a random seed secret.

The value returned by the first ratchet update is stored in gamma.memberSecret as the added user’s initial member secret. The result of the second ratchet update becomes I_sender, the update secret for the sender of the add message. On line 10, if the local user is the sender, the update secret is returned.

If the local user is not the sender, an acknowledgment for the add message is required. Therefore, on line 11, a control message of type add-ack is constructed for broadcast. Subsequently, in line 12 the current ratchet state is encrypted using 2SM to generate a direct message intended for the added user, allowing them to decrypt subsequent messages sent by the sender. Finally, in lines 13 to 15, process-add-ack is called to calculate the local user’s update secret (I_me), which is then returned along with I_sender.

if added = gamma.myId then return process-welcome(gamma, sender, seq, dmsg)
op = ("add", sender, seq, added)
gamma.history = gamma.history + {op}
if gamma.myId in member-view(gamma, sender) then
(gamma, s) = update-ratchet(gamma, sender, "welcome")
gamma.memberSecret[sender, seq, added] = s
(gamma, I_sender) = update-ratchet(gamma, sender, "add")
else I_sender = empty_string
if sender = gamma.myId then return (gamma, empty_string, empty_string,
I_sender, empty_string)
control = ("add-ack", ++gamma.mySeq, (sender, seq))
(gamma, c) = encrypt-to(gamma, added, ratchet[gamma.myId])
(gamma, _, _, I_me, _) = process-add-ack(gamma, gamma.myId, gamma.mySeq,
(sender, seq), empty_string)
return (gamma, control, {(added, c)}, I_sender, I_me)

process-add-ack

This function is invoked by both the sender and each recipient of an add-ack message, including the new group member. Upon lines 1–2, the acknowledgment is added to gamma.history, mirroring the process in process-ack. If the current user is the new group member, the add-ack message includes the direct message constructed in process-add; this direct message contains the encrypted ratchet state of the sender of the add-ack, then it is decrypted on lines 3–5.

Upon line 6, a check is performed to check if the local user was already a group member at the time the add-ack was sent. If affirmative, a new update secret I for the sender of the add-ack is computed on line 7 by invoking update-ratchet with the constant string add.

In the scenario involving the new member, the ratchet state was recently initialized on line 5. This ratchet update facilitates all group members, including the new addition, to derive each member’s update by obtaining any update secret from before their inclusion.

op = ("ack", sender, seq, ackID, ackSeq)
gamma$.history = gamma.history + {op}
if dmsg != empty_string then
(gamma, s) = decrypt-from(gamma, sender, dmsg)
gamma.ratchet[sender] = s
if gamma.myId in member-view(gamma, sender) then
(gamma, I) = update-ratchet(gamma, sender, "add")
return (gamma, empty_string, empty_string, I, empty_string)
else return (gamma, empty_string, empty_string, empty_string, empty_string)

process-welcome

This function serves as the second step called by a newly added group member. In this context, adderHistory represents the adding user’s copy of gamma.history sent in their welcome message, which is utilized to initialize the added user’s history. Here, c denotes the ciphertext of the adding user’s ratchet state, which is decrypted on line 2 using decrypt-from.

Once gamma.ratchet[sender] is initialized, update-ratchet is invoked twice on lines 3 to 5 with the constant strings welcome and add respectively. These operations mirror the ratchet operations performed by every other group member in process-add. The outcome of the first update-ratchet call becomes the first member secret for the added user, while the second call returns I_sender, the update secret for the sender of the add operation.

Subsequently, the new group member constructs an ack control message to broadcast on line 6 and calls process-ack to compute their initial update secret I_me. The function process-ack reads from gamma.memberSecret and passes it to update-ratchet. The previous ratchet state for the new member is the empty string empty, as established by init, thereby initializing the new member’s ratchet. Upon receiving the new member’s ack, every other group member initializes their copy of the new member’s ratchet in a similar manner.

By the conclusion of process-welcome, the new group member has acquired update secrets for themselves and the user who added them. The ratchets for other group members are initialized by process-add-ack.

gamma.history = adderHistory
(gamma, gamma.ratchet[sender]) = decrypt-from(gamma, sender, c)
(gamma, s) = update-ratchet(gamma, sender, "welcome")
gamma.memberSecret[sender, seq, gamma.myId] = s
(gamma, I_sender) = update-ratchet(gamma, sender, "add")
control = ("ack", ++gamma.mySeq, (sender, seq))
(gamma, _, _, I_me, _) = process-ack(gamma, gamma.myId, gamma.mySeq, (sender,
seq), empty_string)
return (gamma, control, empty_string , I_sender, I_me)

Privacy Considerations

Dependency on PKI

The DCGKA proposal presents some limitations highlighted by the authors. Among these limitations one finds the requirement of a PKI (or a key server) mapping IDs to public keys.

One method to overcome this limitation is adapting the protocol SIWE (Sign in with Ethereum) so a user u_1 who wants to start a communication with a user u_2 can interact with latter’s wallet to request a public key using an Ethereum address as ID.

SIWE

The SIWE (Sign In With Ethereum) proposal was a suggested standard for leveraging Ethereum to authenticate and authorize users on web3 applications. Its goal is to establish a standardized method for users to sign in to web3 applications using their Ethereum address and private key, mirroring the process by which users currently sign in to web2 applications using their email and password. Below follows the required steps:

  1. A server generates a unique Nonce for each user intending to sign in.
  2. A user initiates a request to connect to a website using their wallet.
  3. The user is presented with a distinctive message that includes the Nonce and details about the website.
  4. The user authenticates their identity by signing in with their wallet.
  5. Upon successful authentication, the user's identity is confirmed or approved.
  6. The website grants access to data specific to the authenticated user.

Our approach

The idea in the DCGKA setting closely resembles the procedure outlined in SIWE. Here:

  1. The server corresponds to user D1,who initiates a request (instead of generating a nonce) to obtain the public key of user D2.
  2. Upon receiving the request, the wallet of D2 send the request to the user,
  3. User D2 receives the request from the wallet, and decides whether accepts or rejects.
  4. The wallet and responds with a message containing the requested public key in case of acceptance by D2.

This message may be signed, allowing D1 to verify that the owner of the received public key is indeed D2.

Multi-device setting

One may see the set of devices as a group and create a group key for internal communications. One may use treeKEM for instance, since it provides interesting properties like forward secrecy and post-compromise security. All devices share the same ID, which is held by one of them, and from other user’s point of view, they would look as a single user.

Using servers, like in the paper Multi-Device for Signal, should be avoided; but this would imply using a particular device as receiver and broadcaster within the group. There is an obvious drawback which is having a single device working as a “server”. Should this device be attacked or without connection, there should be a mechanism for its revocation and replacement.

Another approach for communications between devices could be using the keypair of each device. This could open the door to use UPKE, since keypairs should be regenerated frequently.

Each time a device sends a message, either an internal message or an external message, it needs to replicate and broadcast it to all devices in the group.

The mechanism for the substitution of misbehaving leader devices follows:

  1. Each device within a group knows the details of other leader devices. This information may come from metadata in received messages, and is replicated by the leader device.
  2. To replace a leader, the user should select any other device within its group and use it to send a signed message to all other users.
  3. To get the ability to sign messages, this new leader should request the keypair associated to the ID to the wallet.
  4. Once the leader has been changed, it revocates access from DCGKA to the former leader using the DCGKA protocol.
  5. The new leader starts a key update in DCGKA.

Not all devices in a group should be able to send messages to other users. Only the leader device should be in charge of sending and receiving messages. To prevent other devices from sending messages outside their group, a requirement should be signing each message. The keys associated to the ID should only be in control of the leader device.

The leader device is in charge of setting the keys involved in the DCGKA. This information must be replicated within the group to make sure it is updated.

To detect missing messages or potential misbehavior, messages must include a counter.

Using UPKE

Managing the group of devices of a user can be done either using a group key protocol such as treeKEM or using the keypair of each device. Setting a common key for a group of devices under the control of the same actor might be excessive, furthermore it may imply some of the problems one can find in the usual setting of a group of different users; for example: one of the devices may not participate in the required updating processes, representing a threat for the group.

The other approach to managing the group of devices is using each device’s keypair, but it would require each device updating these materia frequently, something that may not happens.

UPKE is a form of asymetric cryptography where any user can update any other user’s key pair by running an update algorithm with (high-entropy) private coins. Any sender can initiate a key update by sending a special update ciphertext. This ciphertext updates the receiver’s public key and also, once processed by the receiver, will update their secret key.

To the best of my knowledge, there exists several efficient constructions both UPKE from ElGamal (based in the DH assumption) and UPKE from Lattices (based in lattices). None of them have been implemented in a secure messaging protocol, and this opens the door to some novel research.

Copyright and related rights waived via CC0.

References