-
Notifications
You must be signed in to change notification settings - Fork 0
Generate first DH key for invitee in Double ratchet and send out the first message in conversation. #20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generate first DH key for invitee in Double ratchet and send out the first message in conversation. #20
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ type | |
| ErrorCode* = enum | ||
| errTypeError | ||
| errWrapped | ||
| errDecryptOutgoing | ||
|
|
||
|
|
||
| proc `$`*(x: ChatError): string = | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,8 @@ import | |
| types, | ||
| utils | ||
|
|
||
| import ../content_types | ||
|
|
||
| logScope: | ||
| topics = "chat inbox" | ||
|
|
||
|
|
@@ -42,7 +44,7 @@ proc decrypt*(inbox: Inbox, encbytes: EncryptedPayload): Result[InboxV1Frame, st | |
| result = res_frame | ||
|
|
||
| proc wrap_env*(payload: EncryptedPayload, convo_id: string): WapEnvelopeV1 = | ||
| let bytes = encode(payload) | ||
| let bytes = proto_types.encode(payload) | ||
| let salt = generateSalt() | ||
|
|
||
| return WapEnvelopeV1( | ||
|
|
@@ -87,12 +89,16 @@ proc createPrivateV1FromInvite*[T: ConversationStore](client: T, | |
| topic = convo.getConvoId() | ||
| client.addConversation(convo) | ||
|
|
||
| # TODO send a control frame instead | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure why any frame needs to be sent here. A Recipient ought to be able to initialize an inbound session and receive messages without revealing activity to the sender
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if I miss something, here is my thought: After both run Without sending a control message, if alice sends message to bob, alice will use message key derived from empty send key. This seems not what we want.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
You are correct in that the DH step requires a key from the remote party before it can occur. Alice can use bobs static installation key as the initial Requiring a response from Bob to complete initialization, would remove the non-interactive protocol property |
||
| discard convo.sendMessage(client.ds, initTextFrame("Hello").toContentFrame()) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a tracking ticket to make sendMessage content Agnostic. |
||
|
|
||
|
|
||
| proc handleFrame*[T: ConversationStore](convo: Inbox, client: T, bytes: seq[ | ||
| byte]) = | ||
| ## Dispatcher for Incoming `InboxV1Frames`. | ||
| ## Calls further processing depending on the kind of frame. | ||
|
|
||
| let enc = decode(bytes, EncryptedPayload).valueOr: | ||
| let enc = proto_types.decode(bytes, EncryptedPayload).valueOr: | ||
| raise newException(ValueError, "Failed to decode payload") | ||
|
|
||
| let frame = convo.decrypt(enc).valueOr: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,8 +16,8 @@ const maxSkip = 10 | |
|
|
||
|
|
||
| type Doubleratchet* = object | ||
| dhSelf: PrivateKey | ||
| dhRemote: PublicKey | ||
| dhSelf*: PrivateKey | ||
| dhRemote*: PublicKey | ||
|
|
||
| rootKey: RootKey | ||
| chainKeySend: ChainKey | ||
|
|
@@ -73,15 +73,20 @@ func kdfChain(self: Doubleratchet, chainKey: ChainKey): (MessageKey, ChainKey) = | |
|
|
||
| return(msgKey, chainKey) | ||
|
|
||
| func dhRatchetSend(self: var Doubleratchet) = | ||
| proc dhRatchetSend(self: var Doubleratchet) = | ||
| # Perform DH Ratchet step when receiving a new peer key. | ||
| info "dhRatchetSend DH Self: ", dhSelf = self.dhSelf | ||
| self.dhSelf = generateKeypair().get()[0] | ||
| info "dhRatchetSend new DH Self: ", dhSelf = self.dhSelf | ||
| let dhOutput : DhDerivedKey = dhExchange(self.dhSelf, self.dhRemote).get() | ||
| let (newRootKey, newChainKeySend) = kdfRoot(self, self.rootKey, dhOutput) | ||
| self.rootKey = newRootKey | ||
| self.chainKeySend = newChainKeySend | ||
| self.msgCountSend = 0 | ||
|
|
||
| proc dhRatchetRecv(self: var Doubleratchet, remotePublickey: PublicKey ) = | ||
| info "dh ratchet happens" | ||
| info "dhRatchetRecv DH Remote: ", dhRemote = remotePublickey | ||
| self.prevChainLen = self.msgCountSend | ||
| self.msgCountSend = 0 | ||
| self.msgCountRecv = 0 | ||
|
|
@@ -96,7 +101,7 @@ proc dhRatchetRecv(self: var Doubleratchet, remotePublickey: PublicKey ) = | |
| self.dhSelf = generateKeypair().get()[0] | ||
|
|
||
| let dhOutputPost = self.dhSelf.dhExchange(self.dhRemote).get() | ||
| (self.rootKey, self.chainKeyRecv) = kdfRoot(self, self.rootKey, dhOutputPost) | ||
| (self.rootKey, self.chainKeySend) = kdfRoot(self, self.rootKey, dhOutputPost) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice catch. I'd suggest getting rid of the direct kdf call all togther and use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In a correct implementation, the sendingChainKey is not generated until it is needed to minimize key exposure. dhRatchetRecv: Performs the first part of the ratchet step when receiving One path to clean this up, is to remove keyGeneration and subsequent KeyExchange from this function all together. |
||
|
|
||
|
|
||
| proc skipMessageKeys(self: var Doubleratchet, until: MsgCount): Result[(), string] = | ||
|
|
@@ -138,9 +143,15 @@ proc encrypt(self: var Doubleratchet, plaintext: var seq[byte], associatedData: | |
|
|
||
|
|
||
| proc decrypt*(self: var Doubleratchet, header: DrHeader, ciphertext: CipherText, associatedData: openArray[byte] ) : Result[seq[byte], NaxolotlError] = | ||
| info "double ratchet decrypt", header = $header | ||
| info "dhRemote: ", dhRemote = self.dhRemote | ||
| info "dhSelf: ", dhSelf = self.dhSelf | ||
| info "dhSelf public: ", dhSelf = self.dhSelf.public | ||
|
|
||
| let peerPublic = header.dhPublic | ||
|
|
||
| info "peerPublic: ", peerPublic = peerPublic | ||
|
|
||
| var msgKey : MessageKey | ||
|
|
||
| # Check Skipped Keys | ||
|
|
@@ -176,8 +187,12 @@ proc encrypt*(self: var Doubleratchet, plaintext: var seq[byte]) : (DrHeader, Ci | |
| encrypt(self, plaintext,@[]) | ||
|
|
||
|
|
||
| func initDoubleratchet*(sharedSecret: array[32, byte], dhSelf: PrivateKey, dhRemote: PublicKey, isSending: bool = true): Doubleratchet = | ||
| proc initDoubleratchet*(sharedSecret: array[32, byte], dhSelf: PrivateKey, dhRemote: PublicKey, inviter: bool = true): Doubleratchet = | ||
|
|
||
| info "Initializing Double Ratchet" | ||
| info "DH Self: ", dhSelf = dhSelf | ||
| info "DH Self public: ", dhSelf = dhSelf.public | ||
| info "DH Remote: ", dhRemote = dhRemote | ||
| result = Doubleratchet( | ||
| dhSelf: dhSelf, | ||
| dhRemote: dhRemote, | ||
|
|
@@ -188,5 +203,5 @@ func initDoubleratchet*(sharedSecret: array[32, byte], dhSelf: PrivateKey, dhRem | |
| skippedMessageKeys: initTable[(PublicKey, MsgCount), MessageKey]() | ||
| ) | ||
|
|
||
| if isSending: | ||
| if not inviter: | ||
| result.dhRatchetSend() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think moving away from the "isSender" convention makes sense here.
[Sand] Perhaps "Initiator" would be a better fit as it keeps the language consistent.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When reading the proto definition, I somehow need to think twice to reason the term
participant(initiatorseems fine though). The term inviter/invitee actually looks a bit clear and straightforward to me.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clear unambiguous terms are important. Feel free to suggest new ones here: logos-messaging/specs#80