import std/[tables, sequtils], stew/byteutils, stew/shims/net, chronicles, chronos, confutils, libp2p/crypto/crypto, eth/keys, eth/p2p/discoveryv5/enr import waku/common/logging, waku/node/peer_manager, waku/waku_core, waku/waku_node, waku/waku_enr, waku/waku_discv5, waku/common/protobuf, waku/utils/noise as waku_message_utils, waku/waku_noise/noise_types, waku/waku_noise/noise_utils, waku/waku_noise/noise_handshake_processing, waku/waku_core type AgentKeysAndCommitment* = object staticKey*: noise_types.KeyPair ephemeralKey*: noise_types.KeyPair commitment*: seq[byte] committedStaticKey*: MDigest[256] ContentTopicInfo* = object applicationName*: string applicationVersion*: string shardId*: string proc initAgentKeysAndCommitment* (rng: ref HmacDrbgContext): AgentKeysAndCommitment = let staticKey = genKeyPair(rng[]) let commitment= randomSeqByte(rng[], 32) AgentKeysAndCommitment( staticKey: staticKey, ephemeralKey: genKeyPair(rng[]), commitment: commitment, committedStaticKey: commitPublicKey(getPublicKey(staticKey), commitment)) proc initQr*(rng: ref HmacDrbgContext, contentTopicInfo: ContentTopicInfo, agentInfo: AgentKeysAndCommitment): tuple[qr: string, qrMessageNametag: seq[byte]] = let qr = toQr( contentTopicInfo.applicationName, contentTopicInfo.applicationVersion, contentTopicInfo.shardId, getPublicKey(agentInfo.ephemeralKey), agentInfo.committedStaticKey) let qrMessageNametag = randomSeqByte(rng[], MessageNametagLength) (qr, qrMessageNametag) proc initContentTopicFromQr* (qr: string): ContentTopic = let (readApplicationName, readApplicationVersion, readShardId, _, _) = fromQr(qr) let contentTopic = "/" & readApplicationName & "/" & readApplicationVersion & "/wakunoise/1/sessions_shard-" & readShardId & "/proto" return contentTopic proc initHS*(agentInfo: AgentKeysAndCommitment, qr: string, isInitiator: bool = false): HandshakeState = let hsPattern = NoiseHandshakePatterns["WakuPairing"] (_, _, _, readEphemeralKey, _) = fromQr(qr) preMessagePKs: seq[NoisePublicKey] = @[toNoisePublicKey(readEphemeralKey)] initialize(hsPattern = hsPattern, ephemeralKey = agentInfo.ephemeralKey, staticKey = agentInfo.staticKey, prologue = qr.toBytes, preMessagePKs = preMessagePKs, initiator = isInitiator) proc prepareHandShakeInitiatorMsg*(rng: ref HmacDrbgContext, contentTopic: string, agentInfo: AgentKeysAndCommitment, qrMessageNametag: seq[byte], agentMessageNametag: var MessageNametag, agentHS: var HandshakeState, initiatorStep: var HandshakeStepResult ): Result[WakuMessage, cstring] = ############################## # 1st step # # # # -> eA, eAeB {H(sA||s)}] # ############################## # The messageNametag for the first handshake message is randomly generated # and exchanged out-of-band and corresponds to qrMessageNametag # We set the transport message to be H(sA||s) let transportMessage = digestToSeq(agentInfo.committedStaticKey) # By being the handshake initiator, this agent writes a Waku2 payload v2 # containing handshake message and the (encrypted) transport message # The message is sent with a messageNametag equal to the one received through # the QR code initiatorStep = stepHandshake(rng[], agentHS, transportMessage = transportMessage, messageNametag = qrMessageNametag).get() # We prepare a Waku message from the initiators's payload2 let wakuMsg = encodePayloadV2(initiatorStep.payload2, contentTopic) assert wakuMsg.isOk() assert wakuMsg.get().contentTopic == contentTopic agentMessageNametag = toMessageNametag(agentHS) wakuMsg proc publishHandShakeInitiatorMsg*(node: WakuNode, pubSubTopic: PubsubTopic, contentTopic: ContentTopic, message: WakuMessage) {.async.} = notice "Publishing handshake initiator message", step = 1 await node.publish(some(pubSubTopic), message) notice "Published handshake initiator message", step = 1, psTopic = pubSubTopic, contentTopic = contentTopic, payload = message.payload await sleepAsync(5000) proc handleHandShakeInitiatorMsg*(rng: ref HmacDrbgContext, pubSubTopic: PubsubTopic, contentTopic: ContentTopic, payload: PayloadV2, receiverStep: var HandshakeStepResult, receiverHS: var HandshakeState, receiverMessageNametag: var MessageNametag, qrMessageNametag: seq[byte]) = notice "Received handshake initiator message", step = 1, psTopic = pubSubTopic, contentTopic = contentTopic, payload = payload notice "Handling handshake initiator message", step = 1 # The Receiver reads the Initiator's payloads, and returns the (decrypted) transport # message the Initiator sent to him # Note that the Receiver verifies if the received payloadv2 has the expected messageNametag set receiverStep = stepHandshake(rng[], receiverHS, readPayloadV2 = payload, messageNametag = qrMessageNametag).get() receiverMessageNametag = toMessageNametag(receiverHS) proc prepareHandShakeMsg*(rng: ref HmacDrbgContext, contentTopic: string, agentInfo: AgentKeysAndCommitment, agentMessageNametag: var MessageNametag, agentHS: var HandshakeState, agentStep: var HandshakeStepResult, step: int ): Result[WakuMessage, cstring] = ###################### ########################## # 2nd step # # 3rd step # # # or # # # <- sB, eAsB {r} # # -> sA, sAeB, sAsB {s} # ###################### ########################## notice "Setting up agent and preparing handshake message for step:", step = step let transportMessage = digestToSeq(agentInfo.committedStaticKey) agentStep = stepHandshake(rng[], agentHS, transportMessage = transportMessage, messageNametag = agentMessageNametag).get() let wakuMsg = encodePayloadV2(agentStep.payload2, contentTopic) assert wakuMsg.isOk() assert wakuMsg.get().contentTopic == contentTopic agentMessageNametag = toMessageNametag(agentHS) wakuMsg proc publishHandShakeMsg*(node: WakuNode, pubSubTopic: PubsubTopic, contentTopic: ContentTopic, message: WakuMessage, step: int) {.async.} = notice "Publishing handshake message for step:", step = step await sleepAsync(5000) await node.publish(some(pubSubTopic), message) notice "Published handshake message for step:", step = step, psTopic = pubSubTopic, contentTopic = contentTopic, message = message proc handleHandShakeMsg*(rng: ref HmacDrbgContext, pubSubTopic: PubsubTopic, contentTopic: ContentTopic, step: int, payload: PayloadV2, initiatorStep: var HandshakeStepResult, initiatorHS: var HandshakeState, initiatorMessageNametag: var MessageNametag) = notice "Received handshake message for step:", step = step, psTopic = pubSubTopic, contentTopic = contentTopic, payload = payload notice "Handling handshake message for step:", step = step initiatorStep = stepHandshake(rng[], initiatorHS, readPayloadV2 = payload, messageNametag = initiatorMessageNametag).get() initiatorMessageNametag = toMessageNametag(initiatorHS)