mirror of
https://github.com/logos-messaging/nim-chat-poc.git
synced 2026-01-07 16:43:06 +00:00
Integrate doubleratchet encryption to PrivateV1
This commit is contained in:
parent
8ff8add31d
commit
16aebac0c7
@ -9,7 +9,7 @@ message EncryptedPayload {
|
|||||||
|
|
||||||
oneof encryption {
|
oneof encryption {
|
||||||
encryption.Plaintext plaintext = 1;
|
encryption.Plaintext plaintext = 1;
|
||||||
encryption.Ecies ecies = 2;
|
encryption.Doubleratchet doubleratchet = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,8 +17,10 @@ message Plaintext {
|
|||||||
bytes payload=1;
|
bytes payload=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Ecies {
|
message Doubleratchet {
|
||||||
bytes encrypted_bytes=1;
|
bytes dh = 1; // 32 byte array
|
||||||
bytes ephemeral_pubkey = 2;
|
uint32 msgNum = 2;
|
||||||
bytes tag = 3;
|
uint32 prevChainLen = 3;
|
||||||
|
bytes ciphertext = 4;
|
||||||
|
string aux = 5;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import #local
|
|||||||
conversations/convo_impl,
|
conversations/convo_impl,
|
||||||
crypto,
|
crypto,
|
||||||
delivery/waku_client,
|
delivery/waku_client,
|
||||||
|
errors,
|
||||||
identity,
|
identity,
|
||||||
inbox,
|
inbox,
|
||||||
proto_types,
|
proto_types,
|
||||||
@ -215,7 +216,11 @@ proc newPrivateConversation*(client: Client,
|
|||||||
msgId: string): Future[void] {.async.} =
|
msgId: string): Future[void] {.async.} =
|
||||||
client.notifyDeliveryAck(conversation, msgId)
|
client.notifyDeliveryAck(conversation, msgId)
|
||||||
|
|
||||||
let convo = initPrivateV1(client.identity(), destPubkey, "default", deliveryAckCb)
|
# TODO: remove placeholder key
|
||||||
|
var key : array[32, byte]
|
||||||
|
key[2]=2
|
||||||
|
|
||||||
|
var convo = initPrivateV1Sender(client.identity(), destPubkey, key, deliveryAckCb)
|
||||||
client.addConversation(convo)
|
client.addConversation(convo)
|
||||||
|
|
||||||
# TODO: Subscribe to new content topic
|
# TODO: Subscribe to new content topic
|
||||||
|
|||||||
@ -14,12 +14,15 @@ import ../delivery/waku_client
|
|||||||
|
|
||||||
import ../[
|
import ../[
|
||||||
identity,
|
identity,
|
||||||
|
errors,
|
||||||
proto_types,
|
proto_types,
|
||||||
types,
|
types,
|
||||||
utils
|
utils
|
||||||
]
|
]
|
||||||
import convo_type
|
import convo_type
|
||||||
|
|
||||||
|
import ../../naxolotl as nax
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
PrivateV1* = ref object of Conversation
|
PrivateV1* = ref object of Conversation
|
||||||
@ -29,6 +32,7 @@ type
|
|||||||
topic: string
|
topic: string
|
||||||
participants: seq[PublicKey]
|
participants: seq[PublicKey]
|
||||||
discriminator: string
|
discriminator: string
|
||||||
|
doubleratchet: naxolotl.Doubleratchet
|
||||||
|
|
||||||
proc getTopic*(self: PrivateV1): string =
|
proc getTopic*(self: PrivateV1): string =
|
||||||
## Returns the topic for the PrivateV1 conversation.
|
## Returns the topic for the PrivateV1 conversation.
|
||||||
@ -56,11 +60,32 @@ proc calcMsgId(self: PrivateV1, msgBytes: seq[byte]): string =
|
|||||||
result = getBlake2b(s, 16, "")
|
result = getBlake2b(s, 16, "")
|
||||||
|
|
||||||
|
|
||||||
proc encrypt*(convo: PrivateV1, frame: PrivateV1Frame): EncryptedPayload =
|
proc encrypt*(convo: PrivateV1, plaintext: var seq[byte]): EncryptedPayload =
|
||||||
result = EncryptedPayload(plaintext: Plaintext(payload: encode(frame)))
|
|
||||||
|
let (header, ciphertext) = convo.doubleratchet.encrypt(plaintext) #TODO: Associated Data
|
||||||
|
|
||||||
|
result = EncryptedPayload(doubleratchet: proto_types.DoubleRatchet(
|
||||||
|
dh: toSeq(header.dhPublic),
|
||||||
|
msgNum: header.msgNumber,
|
||||||
|
prevChainLen: header.prevChainLen,
|
||||||
|
ciphertext: ciphertext)
|
||||||
|
)
|
||||||
|
|
||||||
|
proc decrypt*(convo: PrivateV1, enc: EncryptedPayload): Result[seq[byte], ChatError] =
|
||||||
|
# Ensure correct type as received
|
||||||
|
if enc.doubleratchet.ciphertext == @[]:
|
||||||
|
return err(ChatError(code: errTypeError, context: "Expected doubleratchet encrypted payload got ???"))
|
||||||
|
|
||||||
|
let dr = enc.doubleratchet
|
||||||
|
|
||||||
|
var header = DrHeader(
|
||||||
|
msgNumber: dr.msgNum,
|
||||||
|
prevChainLen: dr.prevChainLen
|
||||||
|
)
|
||||||
|
copyMem(addr header.dhPublic[0], unsafeAddr dr.dh[0], dr.dh.len) # TODO: Avoid this copy
|
||||||
|
|
||||||
|
convo.doubleratchet.decrypt(header, dr.ciphertext, @[]).mapErr(proc(e: NaxolotlError): ChatError = ChatError(code: errWrapped, context: repr(e) ))
|
||||||
|
|
||||||
proc decrypt*(convo: PrivateV1, enc: EncryptedPayload): PrivateV1Frame =
|
|
||||||
result = decode(enc.plaintext.payload, PrivateV1Frame).get()
|
|
||||||
|
|
||||||
|
|
||||||
proc wireCallbacks(convo: PrivateV1, deliveryAckCb: proc(
|
proc wireCallbacks(convo: PrivateV1, deliveryAckCb: proc(
|
||||||
@ -91,8 +116,8 @@ proc wireCallbacks(convo: PrivateV1, deliveryAckCb: proc(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc initPrivateV1*(owner: Identity, participant: PublicKey,
|
proc initPrivateV1*(owner: Identity, participant: PublicKey, seedKey: array[32, byte],
|
||||||
discriminator: string = "default", deliveryAckCb: proc(
|
discriminator: string = "default", isSender: bool, deliveryAckCb: proc(
|
||||||
conversation: Conversation,
|
conversation: Conversation,
|
||||||
msgId: string): Future[void] {.async.} = nil):
|
msgId: string): Future[void] {.async.} = nil):
|
||||||
PrivateV1 =
|
PrivateV1 =
|
||||||
@ -107,7 +132,8 @@ proc initPrivateV1*(owner: Identity, participant: PublicKey,
|
|||||||
owner: owner,
|
owner: owner,
|
||||||
topic: derive_topic(participants, discriminator),
|
topic: derive_topic(participants, discriminator),
|
||||||
participants: participants,
|
participants: participants,
|
||||||
discriminator: discriminator
|
discriminator: discriminator,
|
||||||
|
doubleratchet: initDoubleratchet(seedKey, owner.privateKey.bytes, participant.bytes, isSender)
|
||||||
)
|
)
|
||||||
|
|
||||||
result.wireCallbacks(deliveryAckCb)
|
result.wireCallbacks(deliveryAckCb)
|
||||||
@ -115,19 +141,28 @@ proc initPrivateV1*(owner: Identity, participant: PublicKey,
|
|||||||
result.sdsClient.ensureChannel(result.getConvoId()).isOkOr:
|
result.sdsClient.ensureChannel(result.getConvoId()).isOkOr:
|
||||||
raise newException(ValueError, "bad sds channel")
|
raise newException(ValueError, "bad sds channel")
|
||||||
|
|
||||||
|
|
||||||
|
proc initPrivateV1Sender*(owner:Identity, participant: PublicKey, seedKey: array[32, byte], deliveryAckCb: proc(
|
||||||
|
conversation: Conversation, msgId: string): Future[void] {.async.} = nil): PrivateV1 =
|
||||||
|
initPrivateV1(owner, participant, seedKey, "default", true, deliveryAckCb)
|
||||||
|
|
||||||
|
proc initPrivateV1Recipient*(owner:Identity, participant: PublicKey, seedKey: array[32, byte], deliveryAckCb: proc(
|
||||||
|
conversation: Conversation, msgId: string): Future[void] {.async.} = nil): PrivateV1 =
|
||||||
|
initPrivateV1(owner, participant, seedKey, "default", false, deliveryAckCb)
|
||||||
|
|
||||||
|
|
||||||
proc sendFrame(self: PrivateV1, ds: WakuClient,
|
proc sendFrame(self: PrivateV1, ds: WakuClient,
|
||||||
msg: PrivateV1Frame): Future[MessageId]{.async.} =
|
msg: PrivateV1Frame): Future[MessageId]{.async.} =
|
||||||
|
|
||||||
let frameBytes = encode(msg)
|
let frameBytes = encode(msg)
|
||||||
let msgId = self.calcMsgId(frameBytes)
|
let msgId = self.calcMsgId(frameBytes)
|
||||||
let sdsPayload = self.sdsClient.wrapOutgoingMessage(frameBytes, msgId,
|
var sdsPayload = self.sdsClient.wrapOutgoingMessage(frameBytes, msgId,
|
||||||
self.getConvoId()).valueOr:
|
self.getConvoId()).valueOr:
|
||||||
raise newException(ValueError, fmt"sds wrapOutgoingMessage failed: {repr(error)}")
|
raise newException(ValueError, fmt"sds wrapOutgoingMessage failed: {repr(error)}")
|
||||||
|
|
||||||
let encryptedBytes = EncryptedPayload(plaintext: Plaintext(
|
let encryptedPayload = self.encrypt(sdsPayload)
|
||||||
payload: sdsPayload))
|
|
||||||
|
|
||||||
discard ds.sendPayload(self.getTopic(), encryptedBytes.toEnvelope(
|
discard ds.sendPayload(self.getTopic(), encryptedPayload.toEnvelope(
|
||||||
self.getConvoId()))
|
self.getConvoId()))
|
||||||
|
|
||||||
result = msgId
|
result = msgId
|
||||||
@ -144,9 +179,12 @@ proc handleFrame*[T: ConversationStore](convo: PrivateV1, client: T,
|
|||||||
let enc = decode(bytes, EncryptedPayload).valueOr:
|
let enc = decode(bytes, EncryptedPayload).valueOr:
|
||||||
raise newException(ValueError, fmt"Failed to decode EncryptedPayload: {repr(error)}")
|
raise newException(ValueError, fmt"Failed to decode EncryptedPayload: {repr(error)}")
|
||||||
|
|
||||||
# TODO: Decrypt the payload
|
let plaintext = convo.decrypt(enc).valueOr:
|
||||||
|
error "decryption failed", error = error
|
||||||
|
return
|
||||||
|
|
||||||
let (frameData, missingDeps, channelId) = convo.sdsClient.unwrapReceivedMessage(
|
let (frameData, missingDeps, channelId) = convo.sdsClient.unwrapReceivedMessage(
|
||||||
enc.plaintext.payload).valueOr:
|
plaintext).valueOr:
|
||||||
raise newException(ValueError, fmt"Failed to unwrap SDS message:{repr(error)}")
|
raise newException(ValueError, fmt"Failed to unwrap SDS message:{repr(error)}")
|
||||||
|
|
||||||
debug "sds unwrap", convo = convo.id(), missingDeps = missingDeps,
|
debug "sds unwrap", convo = convo.id(), missingDeps = missingDeps,
|
||||||
|
|||||||
14
src/chat_sdk/errors.nim
Normal file
14
src/chat_sdk/errors.nim
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import strformat
|
||||||
|
|
||||||
|
type
|
||||||
|
ChatError* = object of CatchableError
|
||||||
|
code*: ErrorCode
|
||||||
|
context*: string
|
||||||
|
|
||||||
|
ErrorCode* = enum
|
||||||
|
errTypeError
|
||||||
|
errWrapped
|
||||||
|
|
||||||
|
|
||||||
|
proc `$`*(x: ChatError): string =
|
||||||
|
fmt"ChatError(code={$x.code}, context: {x.context})"
|
||||||
@ -78,7 +78,11 @@ proc createPrivateV1FromInvite*[T: ConversationStore](client: T,
|
|||||||
msgId: string): Future[void] {.async.} =
|
msgId: string): Future[void] {.async.} =
|
||||||
client.notifyDeliveryAck(conversation, msgId)
|
client.notifyDeliveryAck(conversation, msgId)
|
||||||
|
|
||||||
let convo = initPrivateV1(client.identity(), destPubkey, "default", deliveryAckCb)
|
# TODO: remove placeholder key
|
||||||
|
var key : array[32, byte]
|
||||||
|
key[2]=2
|
||||||
|
|
||||||
|
let convo = initPrivateV1Recipient(client.identity(), destPubkey, key, deliveryAckCb)
|
||||||
notice "Creating PrivateV1 conversation", client = client.getId(),
|
notice "Creating PrivateV1 conversation", client = client.getId(),
|
||||||
topic = convo.getConvoId()
|
topic = convo.getConvoId()
|
||||||
client.addConversation(convo)
|
client.addConversation(convo)
|
||||||
|
|||||||
@ -1,4 +1 @@
|
|||||||
type ChatError* = string
|
|
||||||
|
|
||||||
|
|
||||||
type MessageId* = string
|
type MessageId* = string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user