diff --git a/vac/raw/decentralized-messaging-ethereum.md b/vac/raw/decentralized-messaging-ethereum.md index 0a433ebb..a5b2af88 100644 --- a/vac/raw/decentralized-messaging-ethereum.md +++ b/vac/raw/decentralized-messaging-ethereum.md @@ -19,28 +19,29 @@ 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. +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. +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: +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 @@ -141,30 +142,29 @@ Two messages `m_1` and `m_2` are causally ordered, or 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}`. +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. +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. +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`. -##### 2SM-Init +##### Function 2SM-Init This function takes two IDs as inputs: `ID1` representing the local user and `ID2` representing the other party. @@ -175,53 +175,56 @@ 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. -##### 2SM-Send +##### 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`. +This function takes a state `sigma` and a plaintext `m` as inputs, and returns +a new state `sigma’` and a ciphertext `c`. -##### 2SM-Receive +##### Function 2SM-Receive This function takes a state `sigma` and a ciphertext `c`, and returns a new state `sigma’` and a plaintext `m`. -#### 2SM Syntax +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: -```rust +```text sigma.mySks[0] = sk sigma.nextIndex = 1 sigma.receivedSk = empty_string sigma.otherPk = pk`
sigma.otherPksender = “other” sigma.otherPkIndex = 0 + ``` -#### The 2SM-Init +#### 2SM-Init On input a key pair `(sk, pk)`, this functions otuputs a state `sigma`. -#### The 2SM-Send +#### 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. +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. +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`. +sending the secret key (encrypted) and storing the public key in +`sigma.otherPk`. -```go +```text sigma.mySks[sigma.nextIndex], myNewPk) = PKE-Gen() (otherNewSk, otherNewPk) = PKE-Gen() plaintext = (m, otherNewSk, sigma`.nextIndex, myNewPk) @@ -229,19 +232,20 @@ msg = (PKE-Enc(sigma.otherPk, plaintext), sigma.otherPkSender, sigma.otherPkInde sigma.nextIndex++ (sigma.otherPk, sigma.otherPkSender, sigma.otherPkIndex) = (otherNewPk, "me", empty_string) return (sigma`, msg) + ``` -#### The 2SM-Receive +#### 2SM-Receive -This function utilizes the metadata of the message `c` -to determine which secret key to utilize for decryption, -assigning it to `sk`. +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. +Subsequently, the new public and secret keys contained in the message are +stored. -```go +```text (ciphertext, keySender, keyIndex) = c if keySender = "other" then sk = sigma.mySks[keyIndex] @@ -250,6 +254,7 @@ else sk = sigma.receivedSk (m, sigma.receivedSk, sigma.otherPkIndex, sigma.otherPk) = PKE-Dec(sk, ciphertext) sigma.otherPkSender = "other" return (sigma, m) + ``` ### PKE Syntax @@ -260,33 +265,36 @@ The required PKE that MUST be used is ElGamal with a 2048-bit modulus `p`. The following parameters must be used: -```go +```text p = 308920927247127345254346920820166145569 g = 2 + ``` #### PKE-KGen Each user `u` MUST do the following: -```go +```text 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: -```go +```text PKE-Enc(pk): k = randint(2, p-2) eta = g^k % p delta = m * (g^a)^k % p return ((eta, delta)) + ``` #### PKE-Dec @@ -294,25 +302,26 @@ return ((eta, delta)) The user `u` recovers a message `m` from a ciphertext `c` by performing the following operations: -```go +```text 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: +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`: -```go +```text gamma.myId = ID gamma.mySeq = 0 gamma.history = empty @@ -321,47 +330,53 @@ 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. +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`. +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`. -```go +```text 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: +After receiving the sender’s `ID` and a ciphertext, it behaves as the reverse +function of `encrypt-to` and has a similar initialization: -```go +```text 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`. +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. +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. -```go +```text (updateSecret, gamma.ratchet[ID]) = HKDF(gamma.ratchet[ID], input) return (gamma, updateSecret) + ``` #### member-view @@ -372,9 +387,10 @@ 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. -```go +```text ops = {m in gamma.history st. m was sent or acknowledged by ID} return DGM(ops) + ``` #### generate-seed @@ -384,132 +400,143 @@ 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. -```go +```text 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. +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. +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`. +The function `process-create` returns a tuple including an updated state gamma +and an update secret `I`. -```go +```text 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. +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. +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 +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. - -```go +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. + +```text 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) +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) +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) +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)} + 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) -(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`. +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`. -```go +```text 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`. +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 +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. +Finally, it updates the ratchet for the sender of the `ack` and returns the +resulting update secret. -```go +```text 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) & (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. @@ -517,13 +544,14 @@ 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. +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`, @@ -531,22 +559,26 @@ whereas `process-remove` includes a removal operation in the history. #### update -```go +```text 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) +(gamma, _, _, I , _) = process-update(gamma, gamma.myId, gamma.mySeq, +empty_string, empty_string) return (gamma, control, dmsgs, I) + ``` #### remove -```go +```text 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) +(gamma, _, _, I , _) = process-update(gamma, gamma.myId, gamma.mySeq, ID, +empty_string) return (gamma, control, dmsgs, I) + ``` #### process-update @@ -555,184 +587,184 @@ return (gamma, control, dmsgs, I) #### process-remove -```go +```text 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 +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. +encrypted using `2SM`, along with the history of group membership operations +conducted so far. -```go +```text 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. +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`. - -```go +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`. + +```text 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) + (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. - -```go +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. + +```text 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 + (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) + (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. +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`. -```go +```text 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) +(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](https://eprint.iacr.org/2020/1281) -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. +The [DCGKA](https://eprint.iacr.org/2020/1281) 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`. +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](https://docs.login.xyz/general-information/siwe-overview) -(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, +The [SIWE](https://docs.login.xyz/general-information/siwe-overview) (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: @@ -742,108 +774,107 @@ Below follows the required steps: 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. +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](https://eprint.iacr.org/2020/1281) -setting closely resembles the procedure outlined in SIWE. Here: +The idea in the [DCGKA](https://eprint.iacr.org/2020/1281) 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. +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. +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. +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](https://eprint.iacr.org/2019/1363), -should be avoided; but this would imply using a particular device as receiver and +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](https://eprint.iacr.org/2019/1363), 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. +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. +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. +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. +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. +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. +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. +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 +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. +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, +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](https://eprint.iacr.org/2022/068) 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](https://eprint.iacr.org/2019/1189) -(based in the DH assumption) and -[UPKE from Lattices]((https://eprint.iacr.org/2023/1400)) (based in lattices). -None of them have been implemented in a secure messaging protocol, and -this opens the door to some novel research. +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](https://eprint.iacr.org/2019/1189) (based in the DH +assumption) and [UPKE from Lattices]((https://eprint.iacr.org/2023/1400)) +(based in lattices). +None of them have been implemented in a secure messaging protocol, and this +opens the door to some novel research. ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via +[CC0](https://creativecommons.org/publicdomain/zero/1.0/). ## References diff --git a/vac/raw/eth-demls.md b/vac/raw/eth-demls.md new file mode 100644 index 00000000..cd3e9c1f --- /dev/null +++ b/vac/raw/eth-demls.md @@ -0,0 +1,1034 @@ +--- +title: ETH-DEMLS +name: Secure channel setup using decentralized MLS and Ethereum accounts +status: raw +category: Standards Track +tags: +editor: Ramses Fernandez +contributors: Aaryamann Challani , Ekaterina Broslavskaya , Ugur Sen , Ksr +--- + +## 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. + +This document specifies a private messaging service using the +Ethereum blockchain as authentication service. +Rooted in the existing [model](../../waku/standards/application/20/toy-eth-pm.md), +this proposal addresses the deficiencies related +to forward privacy and authentication inherent +in the current framework. +The specification is divided into the following sections: + +- Private group messaging protocol, based on the +[MLS protocol](https://datatracker.ietf.org/doc/rfc9420/). +- Specification of an Ethereum-based authentication protocol, based on +[SIWE](https://eips.ethereum.org/EIPS/eip-4361). + +## Protocol flow + +The following steps outline the flow of the protocol. + +### Account Registration and Key Generation + +Each user starts by registering their Ethereum account. +It is used as the authentication service. +Upon registration, the user generates a ``KeyPackage`` +that contains a public key +and supporting metadata required for the MLS group. + +### Group Initialization and Member Management + +When a new group is created, the initiating client generates a new ``GroupContext``. +It contains a unique group ID and an initial epoch. + +To add members, the initiator sends an ``Add`` request, +which includes the new member’s KeyPackage. + +Existing members can update their identity in the group using the ``Update`` proposal, +which replaces the sender’s LeafNode in the group’s ratchet tree. + +Members can be removed from the group via a ``Remove`` proposal, +which specifies the index of the member to be removed from the tree. +Upon processing this proposal, +the group generates a new group key to ensure that removed members +no longer have access to future communications. + +### Commit and Authentication + +After receiving a valid list of proposals (``Add``, ``Update``, ``Remove``), +a client initiates a ``Commit`` message, +processing the pending proposals and updates the group’s state. +The ``Commit`` message includes the updated ``GroupContext`` +and a ``FramedContentAuthData``, +which ensures that all group members are aware of the changes. +Each member verifies the ``FramedContentAuthData`` to ensure the changes are consistent +with the current epoch of the ``GroupContext``. + +### Message Exchange + +Once the group is established and all members have processed the latest ``Commit``, +messages can be securely exchanged using +the session keyderived from the group's ratchet tree. +Each message is encapsulated within a ``FramedContent`` structure +and authenticated using the ``FramedContentAuthData``, +ensuring message integrity. +Group members use the current ``GroupContext`` to validate incoming messages +and ensure they are consistent with the current group state. + +### Use of smart contracts + +This protocol accomplishes decentralization +through the use of smart contracts for managing groups. +They are used to register users in a group and keep the state of the group updated. +Smart contracts MUST include an ACL to keep the state of the group. + +## Private group messaging protocol + +### Background + +The [Messaging Layer Security](https://datatracker.ietf.org/doc/rfc9420/) (MLS) +protocol aims at providing a group of users with +end-to-end encryption in an authenticated and asynchronous way. +The main security characteristics of the protocol are: +Message confidentiality and authentication, sender authentication, +membership agreement, post-remove +and post-update security, and forward secrecy and +post-compromise security. +The MLS protocol achieves: low-complexity, group integrity, +synchronization and extensibility. + +This document describes how the structure and methods of the +[MLS](https://datatracker.ietf.org/doc/rfc9420/) protocol +are extended for their application in decentralized environments. +The approach described in this document makes use of smart contracts. +It makes use of a smart contract to manage each group chat. +Furthermore, this document describes how to use the Sign-in With Ethereum protocol +as authentication method. + +### Structure + +Each MLS session uses a single cipher suite that specifies the +primitives to be used in group key computations. The cipher suite MUST +use: + +- `X488` as Diffie-Hellman function. +- `SHA256` as KDF. +- `AES256-GCM` as AEAD algorithm. +- `SHA512` as hash function. +- `XEd448` for digital signatures. + +Formats for public keys, signatures and public-key encryption MUST +follow Section 5.1 of +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +### Hash-based identifiers + +Some MLS messages refer to other MLS objects by hash. +These identifiers MUST be computed according to Section 5.2 of +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +### Credentials + +Each member of a group presents a credential that provides one or more +identities for the +member and associates them with the member's signing key. +The identities and signing key are verified by the Authentication +Service in use for a group. + +Credentials MUST follow the specifications of section 5.3 of +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +Below follows the flow diagram for the generation of credentials. +Users MUST generate key pairs by themselves. +![figure1](./images/eth-secpm_credential.png) + +### Message framing + +Handshake and application messages use a common framing structure +providing encryption to ensure confidentiality within the group, +and signing to authenticate the sender. + +The structure is: + +- `PublicMessage`: represents a message that is only signed, +and not encrypted. +The definition and the encoding/decoding of a `PublicMessage` +MUST follow the specification in section 6.2 of +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). +- `PrivateMessage`: represents a signed and encrypted message, +with protections for both the content of the message and related metadata. + +The definition, and the encoding/decoding of a `PrivateMessage` +MUST follow the specification in section 6.3 of +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +Applications MUST use `PrivateMessage` to encrypt application messages. + +Applications SHOULD use `PrivateMessage` to encode handshake messages. + +Each encrypted MLS message carries a "generation" number which +is a per-sender incrementing counter. +If a group member observes a gap in the generation sequence +for a sender, then they know that they have missed +a message from that sender. + +### Nodes contents + +> This section makes use of sections 4 and 7 of +[RFC9420](https://datatracker.ietf.org/docrfc9420/). + +The nodes of a ratchet tree +(Section 4 in [RFC9420](https://datatracker.ietf.org/docrfc9420/)) +contain several types of data: + +- Leaf nodes describe individual members. +- Parent nodes describe subgroups. + +Contents of each kind of node, and its structure MUST follow +the indications described in sections 7.1 and 7.2 of +[RFC9420](https://datatracker.ietf.org/docrfc9420/). + +### Leaf node validation + +`KeyPackage` objects describe the client's capabilities and +provides keys that can be used to add the client to a group. + +The validity of a leaf node needs to be verified +at the following stages: + +- When a leaf node is downloaded in a `KeyPackage`, +before it is used to add the client to the group. +- When a leaf node is received by a group member in an +Add, Update, or Commit message. +- When a client validates a ratchet tree. + +A client MUST verify the validity of a leaf node following the +instructions of section 7.3 in +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +### Ratchet tree evolution + +Whenever a member initiates an epoch change, they MAY need to refresh +the key pairs of their leaf and of the nodes on their direct path. +This is done to keep forward secrecy and post-compromise security. +The member initiating the epoch change MUST follow this procedure. +A member updates the nodes along its direct path as follows: + +- Blank all the nodes on the direct path from the leaf to the root. +- Generate a fresh HPKE key pair for the leaf. +- Generate a sequence of path secrets, one for each node on the leaf's +filtered direct path. + +It MUST follow the procedure described in section 7.4 of +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +- Compute the sequence of HPKE key pairs `(node_priv,node_pub)`, +one for each node on the leaf's direct path. + +It MUST follow the procedure described in section 7.4 of +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +### Views of the tree synchronization + +After generating fresh key material and applying it to update +their local tree state, the generator broadcasts this update +to other members of the group. +This operation MUST be done according to section 7.5 of +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +### Leaf synchronization + +Changes to group memberships MUST be represented by +adding and removing leaves of the tree. +This corresponds to increasing or decreasing the depth of the tree, +resulting in the number of leaves being doubled or halved. +These operations MUST be done as described in section 7.7 of +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +### Tree and parent hashing + +Group members can agree on the cryptographic state of the group by +generating a hash value that represents the contents of the group +ratchet tree and the member’s credentials. +The hash of the tree is the hash of its root node, +defined recursively from the leaves. +Tree hashes summarize the state of a tree at point in time. +The hash of a leaf is the hash of the `LeafNodeHashInput` object. +At the same time the hash of a parent node, including the root, +is the hash of a `ParentNodeHashInput` object. +Parent hashes capture information about +how keys in the tree were populated. + +Tree and parent hashing MUST follow the directions +in Sections 7.8 and 7.9 of +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +### Key schedule + +Group keys are derived using the `Extract` and `Expand` functions from +the KDF for the group's cipher suite, +as well as the functions defined below: + +```text +ExpandWithLabel(Secret, Label, Context, Length) = KDF.Expand(Secret, KDFLabel, Length) +DeriveSecret(Secret, Label) = ExpandWithLabel(Secret, Label, "", KDF.Nh) + +``` + +`KDFLabel` MUST be specified as: + +```text +struct { + uint16 length; + opaque label; + opaque context; +} KDFLabel; +``` + +The fields of `KDFLabel` MUST be: + +```text +length = Length; +label = "MLS 1.0 " + Label; +context = Context; + +``` + +Each member of the group MUST maintaint a `GroupContext` object +summarizing the state of the group. + +The sturcture of such object MUST be: + +```text +struct { + ProtocolVersion version = mls10; + CipherSuite cipher_suite; + opaque group_id; + uint64 epoch; + opaque tree_hash; + opaque confirmed_trasncript_hash; + Extension extension; +} GroupContext; +``` + +The use of key scheduling MUST follow the indications +in sections 8.1 - 8.7 in +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +### Secret trees + +For the generation of encryption keys and nonces, the key schedule +begins with the `encryption_secret` at the root and derives a tree of +secrets with the same structure as the group's ratchet tree. +Each leaf in the secret tree is associated with the same group member +as the corresponding leaf in the ratchet tree. + +If `N` is a parent node in the secret tree, the secrets of the children +of `N` MUST be defined following section 9 of +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +#### Encryption keys + +MLS encrypts three different types of information: + +- Metadata (sender information). +- Handshake messages (Proposal and Commit). +- Application messages. + +For handshake and application messages, a sequence of keys is derived +via a sender ratchet. +Each sender has their own sender ratchet, and each step along the +ratchet is called a generation. These procedures MUST follow +section 9.1 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +#### Deletion schedule + +All security-sensitive values MUST be deleted +as soon as they are consumed. + +A sensitive value S is consumed if: + +- S was used to encrypt or (successfully) decrypt a message. +- A key, nonce, or secret derived from S has been consumed. + +The deletion procedure MUST follow the instruction described in +section 9.2 of +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +### Key packages + +KeyPackage objects are used to ease the addition of clients +to a group asynchronously. + +A KeyPackage object specifies: + +- Protocol version and cipher suite supported by the client. +- Public keys that can be used to encrypt Welcome messages. +Welcome messages provide new members with +the information to initialize their state +for the epoch in which they were added +or in which they want to +add themselves to the group +- The content of the leaf node that should be added to the tree to +represent this client. + +KeyPackages are intended to be used only once and SHOULD NOT be reused. + +Clients MAY generate and publish multiple KeyPackages to support +multiple cipher suites. + +The structure of the object MUST be: + +```text +struct { + ProtocolVersion version; + CipherSuite cipher_suite; + HPKEPublicKey init_key; + LeafNode leaf_node; + Extension extensions; + /* SignWithLabel(., "KeyPackageTBS", KeyPackageTBS) */ + opaque signature; +} +``` + +```text +struct { + ProtocolVersion version; + CipheSuite cipher_suite; + HPKEPublicKey init_key; + LeafNode leaf_node; + Extension extensions; +} +``` + +`KeyPackage` object MUST be verified when: + +- A `KeyPackage` is downloaded by a group member, before it is used to +add the client to the group. +- When a `KeyPackage` is received by a group member in an `Add` +message. + +Verification MUST be done as follows: + +- Verify that the cipher suite and protocol version of the `KeyPackage` +match those in the `GroupContext`. +- Verify that the `leaf_node` of the `KeyPackage` is valid for a +`KeyPackage`. +- Verify that the signature on the `KeyPackage` is valid. +- Verify that the value of `leaf_node.encryption_key` is different from +the value of the `init_key field`. + +HPKE public keys are opaque values in a format defined by Section 4 of +[RFC9180](https://datatracker.ietf.org/doc/rfc9180/). + +Signature public keys are represented as opaque values in a format +defined by the cipher suite's signature scheme. + +### Group creation + +A group is always created with a single member. +Other members are then added to the group using the usual +Add/Commit mechanism. +The creator of a group MUST set: + +- the group ID. +- cipher suite. +- initial extensions for the group. + +If the creator intends to add other members at the time of creation, +then it SHOULD fetch `KeyPackages` for those members, and select a +cipher suite and extensions according to their capabilities. + +The creator MUST use the capabilities information in these +`KeyPackages` to verify that the chosen version and cipher suite is the +best option supported by all members. + +Group IDs SHOULD be constructed so they are unique with +high probability. + +To initialize a group, the creator of the group MUST initialize a one +member group with the following initial values: + +- Ratchet tree: A tree with a single node, a leaf node containing an +HPKE public key and credential for the creator. +- Group ID: A value set by the creator. +- Epoch: `0`. +- Tree hash: The root hash of the above ratchet tree. +- Confirmed transcript hash: The zero-length octet string. +- Epoch secret: A fresh random value of size `KDF.Nh`. +- Extensions: Any values of the creator's choosing. + +The creator MUST also calculate the interim transcript hash: + +- Derive the `confirmation_key` for the epoch according to Section 8 of +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). +- Compute a `confirmation_tag` over the empty +`confirmed_transcript_hash` using the `confirmation_key` as described +in Section 8.1 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/). +- Compute the updated `interim_transcript_hash` from the +`confirmed_transcript_hash` and the `confirmation_tag` as described in +Section 8.2 [RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +All members of a group MUST support the cipher suite and protocol +version in use. Additional requirements MAY be imposed by including a +`required_capabilities` extension in the `GroupContext`. + +```text +struct { + ExtensionType extension_types; + ProposalType proposal_types; + CredentialType credential_types; +} +``` + +The flow diagram shows the procedure to fetch key material +from other users: + +![figure2](./images/eth-secpm_fetching.png) + +Below follows the flow diagram for the creation of a group: + +![figure3](./images/eth-secpm_creation.png) + +### Group evolution + +Group membership can change, and existing members can change their keys +in order to achieve post-compromise security. +In MLS, each such change is accomplished by a two-step process: + +- A proposal to make the change is broadcast to the group in a +Proposal message. +- A member of the group or a new member broadcasts a Commit message +that causes one or more proposed changes to enter into effect. + +The group evolves from one cryptographic state to another each time a +Commit message is sent and processed. +These states are called epochs and are uniquely identified among states +of the group by eight-octet epoch values. + +Proposals are included in a `FramedContent` by way of a `Proposal` +structure that indicates their type: + +```text +struct { + ProposalType proposal_type; + select (Proposal.proposal_type) { + case add: Add: + case update: Update; + case remove: Remove; + case psk: PreSharedKey; + case reinit: ReInit; + case external_init: ExternalInit; + case group_context_extensions: GroupContextExtensions; + } +} +``` + +On receiving a `FramedContent` containing a `Proposal`, a client MUST +verify the signature inside `FramedContentAuthData` and that the epoch +field of the enclosing FramedContent is equal to the epoch field of the +current GroupContext object. +If the verification is successful, then the Proposal SHOULD be cached +in such a way that it can be retrieved by hash in a later Commit +message. + +Proposals are organized as follows: + +- `Add`: requests that a client with a specified KeyPackage be added to +the group. +- `Update`: similar to Add, it replaces the sender's LeafNode in the +tree instead of adding a new leaf to the tree. +- `Remove`: requests that the member with the leaf index removed be +removed from the group. +- `ReInit`: requests to reinitialize the group with different +parameters. +- `ExternalInit`: used by new members that want to join a group by +using an external commit. +- `GroupContentExtensions`: it is used to update the list of extensions +in the GroupContext for the group. + +Proposals structure and semantics MUST follow sections 12.1.1 - 12.1.7 +of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +Any list of commited proposals MUST be validated either by a the group +member who created the commit, or any group member processing +such commit. +The validation MUST be done according to one of the procedures +described in Section 12.2 of +[RFC9420](https://datatracker.ietf.orgdoc/rfc9420/). + +When creating or processing a Commit, a client applies a list of +proposals to the ratchet tree and `GroupContext`. +The client MUST apply the proposals in the list in the order described +in Section 12.3 of [RFC9420](https://datatracker.ietf.org/docrfc9420/). + +Below follows the flow diagram for the addition of a member to a group: + +![figure4](./images/eth-secpm_add.png) + +The diagram below shows the procedure to remove a group member: + +![figure5](./images/eth-secpm_remove.png) + +The flow diagram below shows an update procedure: + +![figure6](./images/eth-secpm_update.png) + +### Commit messages + +Commit messages initiate new group epochs. +It informs group members to update their representation of the state of +the group by applying the proposals and advancing the key schedule. + +Each proposal covered by the Commit is included by a +`ProposalOrRef` value. +`ProposalOrRef` identify the proposal to be applied by value +or by reference. +Commits that refer to new Proposals from the committer +can be included by value. +Commits for previously sent proposals from anyone can be sent +by reference. +Proposals sent by reference are specified by including the hash of the +`AuthenticatedContent`. + +Group members that have observed one or more valid proposals within an +epoch MUST send a Commit message before sending application data. +A sender and a receiver of a Commit MUST verify that the committed list +of proposals is valid. +The sender of a Commit SHOULD include all valid proposals received +during the current epoch. + +Functioning of commits MUST follow the instructions of Section 12.4 of +[RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +### Application messages + +Handshake messages provide an authenticated group key exchange +to clients. +To protect application messages sent among the members of a group, the +`encryption_secret` provided by the key schedule is used to derive a +sequence of nonces and keys for message encryption. + +Each client MUST maintain their local copy of the key schedule for each +epoch during which they are a group member. +They derive new keys, nonces, and secrets as needed. This data MUST be +deleted as soon as they have been used. + +Group members MUST use the AEAD algorithm associated with the +negotiated MLS ciphersuite to encrypt and decrypt Application messages +according to the Message Framing section. +The group identifier and epoch allow a device to know which group +secrets should be used and from which Epoch secret to start computing +other secrets and keys. +Application messages SHOULD be padded to provide resistance against +traffic analysis techniques. +This avoids additional information to be provided to an attacker in +order to guess the length of the encrypted message. +Padding SHOULD be used on messages with zero-valued bytes before +AEAD encryption. + +Functioning of application messages MUST follow the instructions of +Section 15 of [RFC9420](https://datatracker.ietf.org/doc/rfc9420/). + +## Implementation of the onchain component of the protocol + +### Assumptions + +- Users have set a secure 1-1 communication channel. +- Each group is managed by a separate smart contract. + +### Addition of members to a group + +1. On-chain: Alice creates a Smart Contract with ACL. +2. Off-chain: Alice sends the contract address +and an invitation message to Bob over the secure channel. +3. Off-chain: Bob sends a signed response +confirming his Ethereum address and agreement to join. +4. Off-chain: Alice verifies the signature using the public key of Bob. +5. On-chain: Alice adds Bob’s address to the ACL. +6. Off-chain: Alice sends a welcome message to Bob. +7. Off-chain: Alice sends a broadcast message to all group members, +notifying them the addition of Bob. + +![figure8](./images/eth-secpm_onchain-register-2.png) + +### Updates in groups + +Removal requests and update requests are considered the same operation. +One assumes Alice is the creator of the contract. +They MUST be processed as follows: + +1. Off-chain: Bob creates a new update request. +2. Off-chain: Bob sends the update request to Alice. +3. Off-chain: Alice verifies the request. +4. On-chain: If the verification is successfull, +Alice sends it to the smart contract for registration. +5. Off-chain: Alice sends a broadcast message +communicating the update to all users. + +![figure9](./images/eth-secpm_onchain-update.png) + +## Ethereum-based authentication protocol + +### Introduction + +Sign-in with Ethereum describes how Ethereum accounts authenticate with +off-chain services by signing a standard message format +parameterized by scope, session details, and security mechanisms. +Sign-in with Ethereum (SIWE), which is described in the +[EIP 4361](https://eips.ethereum.org/EIPS/eip-4361), +MUST be the authentication method required. + +### Pattern + +#### Message format (ABNF) + +A SIWE Message MUST conform with the following Augmented Backus–Naur +Form ([RFC 5234](https://datatracker.ietf.org/doc/html/rfc5234)) +expression. + +```text +sign-in-with-ethereum = + [ scheme "://" ] domain %s" wants you to sign in with your + Ethereum account:" LF address LF + LF + [ statement LF ] + LF + %s"URI: " uri LF + %s"Version: " version LF + %s"Chain ID: " chain-id LF + %s"Nonce: " nonce LF + %s"Issued At: " issued-at + [ LF %s"Expiration Time: " expiration-time ] + [ LF %s"Not Before: " not-before ] + [ LF %s"Request ID: " request-id ] + [ LF %s"Resources:" + resources ] + +scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + ; See RFC 3986 for the fully contextualized + ; definition of "scheme". + +domain = authority + ; From RFC 3986: + ; authority = [ userinfo "@" ] host [ ":" port ] + ; See RFC 3986 for the fully contextualized + ; definition of "authority". + +address = "0x" 40*40HEXDIG + ; Must also conform to captilization + ; checksum encoding specified in EIP-55 + ; where applicable (EOAs). + +statement = *( reserved / unreserved / " " ) + ; See RFC 3986 for the definition + ; of "reserved" and "unreserved". + ; The purpose is to exclude LF (line break). + +uri = URI + ; See RFC 3986 for the definition of "URI". + +version = "1" + +chain-id = 1*DIGIT + ; See EIP-155 for valid CHAIN_IDs. + +nonce = 8*( ALPHA / DIGIT ) + ; See RFC 5234 for the definition + ; of "ALPHA" and "DIGIT". + +issued-at = date-time +expiration-time = date-time +not-before = date-time + ; See RFC 3339 (ISO 8601) for the + ; definition of "date-time". + +request-id = *pchar + ; See RFC 3986 for the definition of "pchar". + +resources = *( LF resource ) + +resource = "- " URI +``` + +This specification defines the following SIWE Message fields that can +be parsed from a SIWE Message by following the rules in +ABNF Message Format. +This section follows the section _ABNF message format_ +in [EIP 4361](https://eips.ethereum.org/EIPS/eip-4361). + +- `scheme` OPTIONAL. The URI scheme of the origin of the request. +Its value MUST be a +[RFC 3986](https://datatracker.ietf.org/doc/htmlrfc3986) +URI scheme. + +- `domain` REQUIRED. +The domain that is requesting the signing. +Its value MUST be a [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) +authority. The authority includes an OPTIONAL port. +If the port is not specified, the default +port for the provided scheme is assumed. + +If scheme is not specified, HTTPS is assumed by default. + +- `address` REQUIRED. The Ethereum address performing the signing. +Its value SHOULD be conformant to mixed-case checksum address encoding +specified in ERC-55 where applicable. + +- `statement` OPTIONAL. A human-readable ASCII assertion that the user +will sign which MUST NOT include '\n' (the byte 0x0a). +- `uri` REQUIRED. An +[RFC 3986](https://datatracker.ietf.org/doc/htmlrfc3986) +URI referring to the resource that is the subject of the +signing. + +- `version` REQUIRED. The current version of the SIWE Message, which +MUST be 1 for this specification. + +- `chain-id` REQUIRED. The EIP-155 Chain ID to which the session is +bound, and the network where Contract Accounts MUST be resolved. + +- `nonce` REQUIRED. A random string (minimum 8 alphanumeric characters) +chosen by the relying party and used to prevent replay attacks. + +- `issued-at` REQUIRED. The time when the message was generated, +typically the current time. + +Its value MUST be an ISO 8601 datetime string. + +- `expiration-time` OPTIONAL. The time when the signed authentication +message is no longer valid. + +Its value MUST be an ISO 8601 datetime string. + +- `not-before` OPTIONAL. The time when the signed authentication +message will become valid. + +Its value MUST be an ISO 8601 datetime string. + +- `request-id` OPTIONAL. An system-specific identifier that MAY be used +to uniquely refer to the sign-in request. + +- `resources` OPTIONAL. A list of information or references to +information the user wishes to have resolved as part of authentication +by the relying party. + +Every resource MUST be a RFC 3986 URI separated by "\n- " where \n is +the byte 0x0a. + +#### Signing and Verifying Messages with Ethereum Accounts + +- For Externally Owned Accounts, the verification method specified in +[ERC-191](https://eips.ethereum.org/EIPS/eip-191) +MUST be used. + +- For Contract Accounts, + + - The verification method specified in +[ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) +SHOULD be used. +Otherwise, the implementer MUST clearly define the +verification method to attain security and interoperability +for both wallets and relying parties. + + - When performing [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) +signature verification, the contract performing the verification MUST +be resolved from the specified `chain-id`. + + - Implementers SHOULD take into consideration that +[ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) +implementations are not required to be pure functions. +They can return different results for the same inputs depending +on blockchain state. +This can affect the security model and session validation rules. + +#### Resolving Ethereum Name Service (ENS) Data + +- The relying party or wallet MAY additionally perform resolution of +ENS data, as this can improve the user experience by displaying human +friendly information that is related to the `address`. +Resolvable ENS data include: + + - The primary ENS name. + - The ENS avatar. + - Any other resolvable resources specified in the ENS documentation. + +- If resolution of ENS data is performed, implementers SHOULD take +precautions to preserve user privacy and consent. +Their `address` could be forwarded to third party services as part of +the resolution process. + +#### Implementer steps: specifying the request origin + +The `domain` and, if present, the `scheme`, in the SIWE Message MUST +correspond to the origin from where the signing request was made. + +#### Implementer steps: verifying a signed message + +The SIWE Message MUST be checked for conformance to the ABNF Message +Format and its signature MUST be checked as defined in Signing and +Verifying Messages with Ethereum Accounts. + +#### Implementer steps: creating sessions + +Sessions MUST be bound to the address and not to further resolved +resources that can change. + +#### Implementer steps: interpreting and resolving resources + +Implementers SHOULD ensure that that URIs in the listed resources are +human-friendly when expressed in plaintext form. + +#### Wallet implementer steps: verifying the message format + +The full SIWE message MUST be checked for conformance to the ABNF +defined in ABNF Message Format. + +Wallet implementers SHOULD warn users if the substring `"wants you to +sign in with your Ethereum account"` appears anywhere in an +[ERC-191](https://eips.ethereum.org/EIPS/eip-191) +message signing request unless the message fully conforms to the format +defined ABNF Message Format. + +#### Wallet implementer steps: verifying the request origin + +Wallet implementers MUST prevent phishing attacks by verifying the +origin of the request against the `scheme` and `domain` fields in the +SIWE Message. + +The origin SHOULD be read from a trusted data source such as the +browser window or over WalletConnect +[ERC-1328](https://eips.ethereum.org/EIPS/eip-1328) sessions for +comparison against the signing message contents. + +Wallet implementers MAY warn instead of rejecting the verification if +the origin is pointing to localhost. + +The following is a RECOMMENDED algorithm for Wallets to conform with +the requirements on request origin verification defined by +this specification. + +The algorithm takes the following input variables: + +- fields from the SIWE message. +- `origin` of the signing request: the origin of the page which +requested the signin via the provider. +- `allowedSchemes`: a list of schemes allowed by the Wallet. +- `defaultScheme`: a scheme to assume when none was provided. Wallet +implementers in the browser SHOULD use https. +- developer mode indication: a setting deciding if certain risks should +be a warning instead of rejection. Can be manually configured or +derived from `origin` being localhost. + +The algorithm is described as follows: + +- If `scheme` was not provided, then assign `defaultScheme` as scheme. +- If `scheme` is not contained in `allowedSchemes`, then the `scheme` +is not expected and the Wallet MUST reject the request. +Wallet implementers in the browser SHOULD limit the list of +allowedSchemes to just 'https' unless a developer mode is activated. +- If `scheme` does not match the scheme of origin, the Wallet SHOULD +reject the request. +Wallet implementers MAY show a warning instead of rejecting the request +if a developer mode is activated. +In that case the Wallet continues processing the request. +- If the `host` part of the `domain` and `origin` do not match, the +Wallet MUST reject the request unless the Wallet is in developer mode. +In developer mode the Wallet MAY show a warning instead and continues +procesing the request. +- If `domain` and `origin` have mismatching subdomains, the Wallet +SHOULD reject the request unless the Wallet is in developer mode. +In developer mode the Wallet MAY show a warning instead and continues +procesing the request. +- Let `port` be the port component of `domain`, and if no port is +contained in domain, assign port the default port specified for the +scheme. +- If `port` is not empty, then the Wallet SHOULD show a warning if the +`port` does not match the port of `origin`. +- If `port` is empty, then the Wallet MAY show a warning if `origin` +contains a specific port. +- Return request origin verification completed. + +#### Wallet implementer steps: creating SIWE interfaces + +Wallet implementers MUST display to the user the following fields from +the SIWE Message request by default and prior to signing, if they are +present: `scheme`, `domain`, `address`, `statement`, and `resources`. +Other present fields MUST also be made available to the user prior to +signing either by default or through an extended interface. + +Wallet implementers displaying a plaintext SIWE Message to the user +SHOULD require the user to scroll to the bottom of the text area +prior to signing. + +Wallet implementers MAY construct a custom SIWE user interface by +parsing the ABNF terms into data elements for use in the interface. +The display rules above still apply to custom interfaces. + +#### Wallet implementer steps: supporting internationalization (i18n) + +After successfully parsing the message into ABNF terms, translation MAY +happen at the UX level per human language. + +## Consideration to secure 1-to-1 channels + +There are situations where users need to set one-to-one communication +channels in a secure way. One of these situations is when a user A +wants to add a new user B to an existing group. +In such situations communications between users MUST be done following +the instructions in this +[specification](https://github.com/vacp2p/rfc-index/blob/eth-secpm-splitted/vac/raw/eth-secure-channel.md) +describing the use of X3DH in combination with the double ratchet mechanism. + +## Considerations with respect to decentralization + +The MLS protocol assumes the existence of a (central, untrusted) +*delivery service*, whose responsabilites include: + +- Acting as a directory service providing the initial +keying material for clients to use. +- Routing MLS messages among clients. + +The central delivery service can be avoided in protocols using the +publish/gossip approach, such as +[gossipsub](https://github.com/libp2p/specs/tree/master/pubsub/gossipsub). + +Concerning keys, each node can generate and disseminate their +encryption key among the other nodes, so they can create a local +version of the tree that allows for the generation of the group key. + +Another important component is the _authentication service_, which is +replaced with SIWE in this specification. + +## Privacy and Security Considerations + +- For the information retrieval, the algorithm MUST include a access +control mechanisms to restrict who can call the set and get functions. +- One SHOULD include event logs to track changes in public keys. +- The curve vurve448 MUST be chosen due to its higher security level: +224-bit security instead of the 128-bit security provided by X25519. +- It is important that Bob MUST NOT reuse `SPK`. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + +## References + +- [Augmented BNF for Syntax Specifications](https://datatracker.ietf.org/doc/html/rfc5234) +- [Gossipsub](https://github.com/libp2p/specs/tree/master/pubsub/gossipsub) +- [HMAC-based Extract-and-Expand Key Derivation Function](https://www.ietf.org/rfc/rfc5869.txt) +- [Hybrid Public Key Encryption](https://datatracker.ietf.org/doc/rfc9180/) +- [Security Analysis and Improvements for the IETF MLS Standard for Group Messaging](https://eprint.iacr.org/2019/1189.pdf) +- [Signed Data Standard](https://eips.ethereum.org/EIPS/eip-191) +- [Sign-In with Ethereum](https://eips.ethereum.org/EIPS/eip-4361) +- [Standard Signature Validation Method for Contracts](https://eips.ethereum.org/EIPS/eip-1271) +- [The Messaging Layer Security Protocol](https://datatracker.ietf.org/doc/rfc9420/) +- [Toy Ethereum Private Messaging Protocol](https://rfc.vac.dev/spec/20/) +- [Uniform Resource Identifier](https://datatracker.ietf.org/doc/html/rfc3986) +- [WalletConnect URI Format](https://eips.ethereum.org/EIPS/eip-1328) diff --git a/vac/raw/eth-secure-channel.md b/vac/raw/eth-secure-channel.md new file mode 100644 index 00000000..a6b78793 --- /dev/null +++ b/vac/raw/eth-secure-channel.md @@ -0,0 +1,345 @@ +--- +title: ETH-SECURE-CHANNEL +name: Secure 1-to-1 channel setup using X3DH and the double ratchet +status: raw +category: Standards Track +tags: +editor: Ramses Fernandez +contributors: +--- + +## Motivation + +The need for secure communications has become paramount. +This specification outlines a protocol describing a +secure 1-to-1 comunication channel between 2 users. The +main components are the X3DH key establishment mechanism, +combined with the double ratchet. The aim of this +combination of schemes is providing a protocol with both +forward secrecy and post-compromise security. + +## Theory + +The specification is based on the noise protocol framework. +It corresponds to the double ratchet scheme combined with +the X3DH algorithm, which will be used to initialize the former. +We chose to express the protocol in noise to be be able to use +the noise streamlined implementation and proving features. +The X3DH algorithm provides both authentication and forward +secrecy, as stated in the +[X3DH specification](https://signal.org/docs/specifications/x3dh/). + +This protocol will consist of several stages: + +1. Key setting for X3DH: this step will produce +prekey bundles for Bob which will be fed into X3DH. +It will also allow Alice to generate the keys required +to run the X3DH algorithm correctly. +2. Execution of X3DH: This step will output +a common secret key `SK` together with an additional +data vector `AD`. Both will be used in the double +ratchet algorithm initialization. +3. Execution of the double ratchet algorithm +for forward secure, authenticated communications, +using the common secret key `SK`, obtained from X3DH, as a root key. + +The protocol assumes the following requirements: + +- Alice knows Bob’s Ethereum address. +- Bob is willing to participate in the protocol, +and publishes his public key. +- Bob’s ownership of his public key is verifiable, +- Alice wants to send message M to Bob. +- An eavesdropper cannot read M’s content +even if she is storing it or relaying it. + +## Syntax + +### Cryptographic suite + +The following cryptographic functions MUST be used: + +- `X488` as Diffie-Hellman function `DH`. +- `SHA256` as KDF. +- `AES256-GCM` as AEAD algorithm. +- `SHA512` as hash function. +- `XEd448` for digital signatures. + +### X3DH initialization + +This scheme MUST work on the curve curve448. +The X3DH algorithm corresponds to the IX pattern in Noise. + +Bob and Alice MUST define personal key pairs +`(ik_B, IK_B)` and `(ik_A, IK_A)` respectively where: + +- The key `ik` must be kept secret, +- and the key `IK` is public. + +Bob MUST generate new keys using +`(ik_B, IK_B) = GENERATE_KEYPAIR(curve = curve448)`. + +Bob MUST also generate a public key pair +`(spk_B, SPK_B) = GENERATE_KEYPAIR(curve = curve448)`. + +`SPK` is a public key generated and stored at medium-term. +Both signed prekey and the certificate MUST +undergo periodic replacement. +After replacing the key, +Bob keeps the old private key of `SPK` +for some interval, dependant on the implementation. +This allows Bob to decrypt delayed messages. + +Bob MUST sign `SPK` for authentication: +`SigSPK = XEd448(ik, Encode(SPK))` + +A final step requires the definition of +`prekey_bundle = (IK, SPK, SigSPK, OPK_i)` + +One-time keys `OPK` MUST be generated as +`(opk_B, OPK_B) = GENERATE_KEYPAIR(curve = curve448)`. + +Before sending an initial message to Bob, +Alice MUST generate an AD: `AD = Encode(IK_A) || Encode(IK_B)`. + +Alice MUST generate ephemeral key pairs +`(ek, EK) = GENERATE_KEYPAIR(curve = curve448)`. + +The function `Encode()` transforms a +curve448 public key into a byte sequence. +This is specified in the [RFC 7748](http://www.ietf.org/rfc/rfc7748.txt) +on elliptic curves for security. + +One MUST consider `q = 2^446 - 13818066809895115352007386748515426880336692474882178609894547503885` +for digital signatures with `(XEd448_sign, XEd448_verify)`: + +```text +XEd448_sign((ik, IK), message): + Z = randbytes(64) + r = SHA512(2^456 - 2 || ik || message || Z ) + R = (r * convert_mont(5)) % q + h = SHA512(R || IK || M) + s = (r + h * ik) % q + return (R || s) +``` + +```text +XEd448_verify(u, message, (R || s)): + if (R.y >= 2^448) or (s >= 2^446): return FALSE + h = (SHA512(R || 156326 || message)) % q + R_check = s * convert_mont(5) - h * 156326 + if R == R_check: return TRUE + return FALSE +``` + +```text +convert_mont(u): + u_masked = u % mod 2^448 + inv = ((1 - u_masked)^(2^448 - 2^224 - 3)) % (2^448 - 2^224 - 1) + P.y = ((1 + u_masked) * inv)) % (2^448 - 2^224 - 1) + P.s = 0 + return P +``` + +### Use of X3DH + +This specification combines the double ratchet +with X3DH using the following data as initialization for the former: + +- The `SK` output from X3DH becomes the `SK` +input of the double ratchet. See section 3.3 of +[Signal Specification](https://signal.org/docs/specifications/doubleratchet/) +for a detailed description. +- The `AD` output from X3DH becomes the `AD` +input of the double ratchet. See sections 3.4 and 3.5 of +[Signal Specification](https://signal.org/docs/specifications/doubleratchet/) +for a detailed description. +- Bob’s signed prekey `SigSPKB` from X3DH is used as Bob’s +initial ratchet public key of the double ratchet. + +X3DH has three phases: + +1. Bob publishes his identity key and prekeys to a server, +a network, or dedicated smart contract. +2. Alice fetches a prekey bundle from the server, +and uses it to send an initial message to Bob. +3. Bob receives and processes Alice's initial message. + +Alice MUST perform the following computations: + +```text +dh1 = DH(IK_A, SPK_B, curve = curve448) +dh2 = DH(EK_A, IK_B, curve = curve448) +dh3 = DH(EK_A, SPK_B) +SK = KDF(dh1 || dh2 || dh3) +``` + +Alice MUST send to Bob a message containing: + +- `IK_A, EK_A`. +- An identifier to Bob's prekeys used. +- A message encrypted with AES256-GCM using `AD` and `SK`. + +Upon reception of the initial message, Bob MUST: + +1. Perform the same computations above with the `DH()` function. +2. Derive `SK` and construct `AD`. +3. Decrypt the initial message encrypted with `AES256-GCM`. +4. If decryption fails, abort the protocol. + +### Initialization of the double datchet + +In this stage Bob and Alice have generated key pairs +and agreed a shared secret `SK` using X3DH. + +Alice calls `RatchetInitAlice()` defined below: + +```text +RatchetInitAlice(SK, IK_B): + state.DHs = GENERATE_KEYPAIR(curve = curve448) + state.DHr = IK_B + state.RK, state.CKs = HKDF(SK, DH(state.DHs, state.DHr)) + state.CKr = None + state.Ns, state.Nr, state.PN = 0 + state.MKSKIPPED = {} +``` + +The HKDF function MUST be the proposal by +[Krawczyk and Eronen](http://www.ietf.org/rfc/rfc5869.txt). +In this proposal `chaining_key` and `input_key_material` +MUST be replaced with `SK` and the output of `DH` respectively. + +Similarly, Bob calls the function `RatchetInitBob()` defined below: + +```text +RatchetInitBob(SK, (ik_B,IK_B)): + state.DHs = (ik_B, IK_B) + state.Dhr = None + state.RK = SK + state.CKs, state.CKr = None + state.Ns, state.Nr, state.PN = 0 + state.MKSKIPPED = {} +``` + +### Encryption + +This function performs the symmetric key ratchet. + +```text +RatchetEncrypt(state, plaintext, AD): + state.CKs, mk = HMAC-SHA256(state.CKs) + header = HEADER(state.DHs, state.PN, state.Ns) + state.Ns = state.Ns + 1 + return header, AES256-GCM_Enc(mk, plaintext, AD || header) +``` + +The `HEADER` function creates a new message header +containing the public key from the key pair output of the `DH`function. +It outputs the previous chain length `pn`, +and the message number `n`. +The returned header object contains ratchet public key +`dh` and integers `pn` and `n`. + +### Decryption + +The function `RatchetDecrypt()` decrypts incoming messages: + +```text +RatchetDecrypt(state, header, ciphertext, AD): + plaintext = TrySkippedMessageKeys(state, header, ciphertext, AD) + if plaintext != None: + return plaintext + if header.dh != state.DHr: + SkipMessageKeys(state, header.pn) + DHRatchet(state, header) + SkipMessageKeys(state, header.n) + state.CKr, mk = HMAC-SHA256(state.CKr) + state.Nr = state.Nr + 1 + return AES256-GCM_Dec(mk, ciphertext, AD || header) +``` + +Auxiliary functions follow: + +```text +DHRatchet(state, header): + state.PN = state.Ns + state.Ns = state.Nr = 0 + state.DHr = header.dh + state.RK, state.CKr = HKDF(state.RK, DH(state.DHs, state.DHr)) + state.DHs = GENERATE_KEYPAIR(curve = curve448) + state.RK, state.CKs = HKDF(state.RK, DH(state.DHs, state.DHr)) +``` + +```text +SkipMessageKeys(state, until): + if state.NR + MAX_SKIP < until: + raise Error + if state.CKr != none: + while state.Nr < until: + state.CKr, mk = HMAC-SHA256(state.CKr) + state.MKSKIPPED[state.DHr, state.Nr] = mk + state.Nr = state.Nr + 1 +``` + +```text +TrySkippedMessageKey(state, header, ciphertext, AD): + if (header.dh, header.n) in state.MKSKIPPED: + mk = state.MKSKIPPED[header.dh, header.n] + delete state.MKSKIPPED[header.dh, header.n] + return AES256-GCM_Dec(mk, ciphertext, AD || header) + else: return None +``` + +## Information retrieval + +### Static data + +Some data, such as the key pairs `(ik, IK)` for Alice and Bob, +MAY NOT be regenerated after a period of time. +Therefore the prekey bundle MAY be stored in long-term +storage solutions, such as a dedicated smart contract +which outputs such a key pair when receiving an Ethereum wallet +address. + +Storing static data is done using a dedicated +smart contract `PublicKeyStorage` which associates +the Ethereum wallet address of a user with his public key. +This mapping is done by `PublicKeyStorage` +using a `publicKeys` function, or a `setPublicKey` function. +This mapping is done if the user passed an authorization process. +A user who wants to retrieve a public key associated +with a specific wallet address calls a function `getPublicKey`. +The user provides the wallet address as the only +input parameter for `getPublicKey`. +The function outputs the associated public key +from the smart contract. + +### Ephemeral data + +Storing ephemeral data on Ethereum MAY be done using +a combination of on-chain and off-chain solutions. +This approach provides an efficient solution to +the problem of storing updatable data in Ethereum. + +1. Ethereum stores a reference or a hash +that points to the off-chain data. +2. Off-chain solutions can include systems like IPFS, +traditional cloud storage solutions, or +decentralized storage networks such as a +[Swarm](https://www.ethswarm.org). + +In any case, the user stores the associated +IPFS hash, URL or reference in Ethereum. + +The fact of a user not updating the ephemeral information +can be understood as Bob not willing to participate in any +communication. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + +## References + +- [The Double Ratchet Algorithm](https://signal.org/docs/specifications/doubleratchet/) +- [The X3DH Key Agreement Protocol](https://signal.org/docs/specifications/x3dh/) diff --git a/vac/raw/images/eth-secpm_onchain-update.png b/vac/raw/images/eth-secpm_onchain-update.png index fe79f219..fac47a5f 100644 Binary files a/vac/raw/images/eth-secpm_onchain-update.png and b/vac/raw/images/eth-secpm_onchain-update.png differ