mirror of
https://github.com/simplex-chat/simplexmq.git
synced 2026-10-23 15:12:26 +00:00
remove public header from all messages, fix envelope sizes (#251)
* fix envelope sizes * only send sender DH pub key for per-queue E2E with confirmation message
This commit is contained in:
1 parent
83d085cadc
commit
4a73a7ecd4
10 files changed
+80
-86
No files matched your search
@@ -332,7 +332,7 @@ rejectContact' _ contactConnId invId =
|
||||
processConfirmation :: AgentMonad m => AgentClient -> RcvQueue -> SMPConfirmation -> m ()
|
||||
processConfirmation c rq@RcvQueue {e2ePrivKey} SMPConfirmation {senderKey, e2ePubKey} = do
|
||||
let dhSecret = C.dh' e2ePubKey e2ePrivKey
|
||||
withStore $ \st -> setRcvQueueConfirmedE2E st rq e2ePubKey dhSecret
|
||||
withStore $ \st -> setRcvQueueConfirmedE2E st rq dhSecret
|
||||
secureQueue c rq senderKey
|
||||
withStore $ \st -> setRcvQueueStatus st rq Secured
|
||||
|
||||
@@ -522,39 +522,38 @@ processSMPTransmission c@AgentClient {subQ} (srv, rId, cmd) = do
|
||||
_ -> atomically $ writeTBQueue subQ ("", "", ERR $ CONN NOT_FOUND)
|
||||
where
|
||||
processSMP :: SConnType c -> ConnData -> RcvQueue -> m ()
|
||||
processSMP cType ConnData {connId} rq@RcvQueue {rcvDhSecret, e2ePrivKey, e2eShared, status} =
|
||||
processSMP cType ConnData {connId} rq@RcvQueue {rcvDhSecret, e2ePrivKey, e2eDhSecret, status} =
|
||||
case cmd of
|
||||
SMP.MSG srvMsgId srvTs msgBody' -> handleNotifyAck $ do
|
||||
-- TODO deduplicate with previously received
|
||||
msgBody <- agentCbDecrypt rcvDhSecret (C.cbNonce srvMsgId) msgBody'
|
||||
encMessage@SMP.EncMessage {emHeader = SMP.PubHeader v e2ePubKey} <-
|
||||
encMessage@SMP.EncMessage {emHeader = SMP.PubHeader v e2ePubKey_} <-
|
||||
liftEither $ parse smpP (AGENT A_MESSAGE) msgBody
|
||||
case e2eShared of
|
||||
Nothing -> do
|
||||
let e2eDhSecret = C.dh' e2ePubKey e2ePrivKey
|
||||
case (e2eDhSecret, e2ePubKey_) of
|
||||
(Nothing, Just e2ePubKey) -> do
|
||||
let e2eDh = C.dh' e2ePubKey e2ePrivKey
|
||||
(_, agentMessage) <-
|
||||
decryptAgentMessage e2eDhSecret encMessage
|
||||
decryptAgentMessage e2eDh encMessage
|
||||
case agentMessage of
|
||||
AgentConfirmation senderKey connInfo -> do
|
||||
smpConfirmation SMPConfirmation {senderKey, e2ePubKey, connInfo}
|
||||
ack
|
||||
AgentInvitation cReq cInfo -> smpInvitation cReq cInfo >> ack
|
||||
_ -> prohibited >> ack
|
||||
Just (e2eSndPubKey, e2eDhSecret)
|
||||
| e2eSndPubKey /= e2ePubKey -> prohibited >> ack
|
||||
| otherwise -> do
|
||||
(msg, agentMessage) <-
|
||||
decryptAgentMessage e2eDhSecret encMessage
|
||||
case agentMessage of
|
||||
AgentMessage AHeader {sndMsgId, prevMsgHash} aMsg -> case aMsg of
|
||||
HELLO -> helloMsg >> ack
|
||||
REPLY cReq -> replyMsg cReq >> ack
|
||||
A_MSG body -> do
|
||||
-- note that there is no ACK sent here, it is sent with agent's user ACK command
|
||||
-- TODO add hash to other messages
|
||||
let msgHash = C.sha256Hash msg
|
||||
agentClientMsg prevMsgHash sndMsgId (srvMsgId, systemToUTCTime srvTs) body msgHash
|
||||
_ -> prohibited >> ack
|
||||
(Just e2eDh, Nothing) -> do
|
||||
(msg, agentMessage) <-
|
||||
decryptAgentMessage e2eDh encMessage
|
||||
case agentMessage of
|
||||
AgentMessage AHeader {sndMsgId, prevMsgHash} aMsg -> case aMsg of
|
||||
HELLO -> helloMsg >> ack
|
||||
REPLY cReq -> replyMsg cReq >> ack
|
||||
A_MSG body -> do
|
||||
-- note that there is no ACK sent here, it is sent with agent's user ACK command
|
||||
-- TODO add hash to other messages
|
||||
let msgHash = C.sha256Hash msg
|
||||
agentClientMsg prevMsgHash sndMsgId (srvMsgId, systemToUTCTime srvTs) body msgHash
|
||||
_ -> prohibited >> ack
|
||||
_ -> prohibited >> ack
|
||||
SMP.END -> do
|
||||
removeSubscription c connId
|
||||
logServer "<--" c srv rId "END"
|
||||
@@ -576,8 +575,8 @@ processSMPTransmission c@AgentClient {subQ} (srv, rId, cmd) = do
|
||||
ack = sendAck c rq
|
||||
|
||||
decryptAgentMessage :: C.DhSecretX25519 -> SMP.EncMessage -> m (ByteString, AgentMessage)
|
||||
decryptAgentMessage e2eDhSecret SMP.EncMessage {emNonce, emBody} = do
|
||||
msg <- agentCbDecrypt e2eDhSecret emNonce emBody
|
||||
decryptAgentMessage e2eDh SMP.EncMessage {emNonce, emBody} = do
|
||||
msg <- agentCbDecrypt e2eDh emNonce emBody
|
||||
agentMessage <-
|
||||
liftEither $ clientToAgentMsg =<< parse smpP (AGENT A_MESSAGE) msg
|
||||
pure (msg, agentMessage)
|
||||
@@ -699,7 +698,6 @@ newSndQueue_ a (SMPQueueUri smpServer senderId clientVersion rcvE2ePubDhKey) cIn
|
||||
{ server = smpServer,
|
||||
sndId = senderId,
|
||||
sndPrivateKey,
|
||||
e2ePubKey,
|
||||
e2eDhSecret = C.dh' rcvE2ePubDhKey e2ePrivKey,
|
||||
status = New
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ newRcvQueue_ a c srv = do
|
||||
rcvPrivateKey,
|
||||
rcvDhSecret = C.dh' rcvPublicDhKey privDhKey,
|
||||
e2ePrivKey,
|
||||
e2eShared = Nothing,
|
||||
e2eDhSecret = Nothing,
|
||||
sndId = Just sndId,
|
||||
status = New
|
||||
}
|
||||
@@ -305,14 +305,14 @@ logSecret :: ByteString -> ByteString
|
||||
logSecret bs = encode $ B.take 3 bs
|
||||
|
||||
sendConfirmation :: forall m. AgentMonad m => AgentClient -> SndQueue -> SMPConfirmation -> m ()
|
||||
sendConfirmation c sq@SndQueue {server, sndId} SMPConfirmation {senderKey, connInfo} =
|
||||
sendConfirmation c sq@SndQueue {server, sndId} SMPConfirmation {senderKey, e2ePubKey, connInfo} =
|
||||
withLogSMP_ c server sndId "SEND <KEY>" $ \smp -> do
|
||||
msg <- mkConfirmation
|
||||
liftSMP $ sendSMPMessage smp Nothing sndId msg
|
||||
where
|
||||
mkConfirmation :: m MsgBody
|
||||
mkConfirmation =
|
||||
agentCbEncrypt sq . serializeAgentMessage $
|
||||
agentCbEncrypt sq (Just e2ePubKey) . serializeAgentMessage $
|
||||
AgentConfirmation senderKey connInfo
|
||||
|
||||
sendHello :: forall m. AgentMonad m => AgentClient -> SndQueue -> RetryInterval -> m ()
|
||||
@@ -326,7 +326,7 @@ sendHello c sq@SndQueue {server, sndId, sndPrivateKey} ri =
|
||||
where
|
||||
mkHello :: m ByteString
|
||||
mkHello = do
|
||||
agentCbEncrypt sq . serializeAgentMessage $
|
||||
agentCbEncrypt sq Nothing . serializeAgentMessage $
|
||||
AgentMessage (AHeader 0 "") HELLO
|
||||
|
||||
sendInvitation :: forall m. AgentMonad m => AgentClient -> SMPQueueUri -> ConnectionRequest 'CMInvitation -> ConnInfo -> m ()
|
||||
@@ -363,11 +363,11 @@ deleteQueue c RcvQueue {server, rcvId, rcvPrivateKey} =
|
||||
sendAgentMessage :: AgentMonad m => AgentClient -> SndQueue -> ByteString -> m ()
|
||||
sendAgentMessage c sq@SndQueue {server, sndId, sndPrivateKey} msg =
|
||||
withLogSMP_ c server sndId "SEND <message>" $ \smp -> do
|
||||
msg' <- agentCbEncrypt sq msg
|
||||
msg' <- agentCbEncrypt sq Nothing msg
|
||||
liftSMP $ sendSMPMessage smp (Just sndPrivateKey) sndId msg'
|
||||
|
||||
agentCbEncrypt :: AgentMonad m => SndQueue -> ByteString -> m ByteString
|
||||
agentCbEncrypt SndQueue {e2ePubKey, e2eDhSecret} msg = do
|
||||
agentCbEncrypt :: AgentMonad m => SndQueue -> Maybe C.PublicKeyX25519 -> ByteString -> m ByteString
|
||||
agentCbEncrypt SndQueue {e2eDhSecret} e2ePubKey msg = do
|
||||
emNonce <- liftIO C.randomCbNonce
|
||||
emBody <-
|
||||
liftEither . first cryptoError $
|
||||
@@ -385,7 +385,7 @@ agentCbEncryptOnce dhRcvPubKey msg = do
|
||||
liftEither . first cryptoError $
|
||||
C.cbEncrypt e2eDhSecret emNonce msg SMP.e2eEncMessageLength
|
||||
-- TODO per-queue client version
|
||||
let emHeader = SMP.PubHeader (maxVersion SMP.smpClientVersion) dhSndPubKey
|
||||
let emHeader = SMP.PubHeader (maxVersion SMP.smpClientVersion) (Just dhSndPubKey)
|
||||
pure $ smpEncode SMP.EncMessage {emHeader, emNonce, emBody}
|
||||
|
||||
agentCbDecrypt :: AgentMonad m => C.DhSecretX25519 -> C.CbNonce -> ByteString -> m ByteString
|
||||
|
||||
@@ -44,7 +44,7 @@ class Monad m => MonadAgentStore s m where
|
||||
upgradeRcvConnToDuplex :: s -> ConnId -> SndQueue -> m ()
|
||||
upgradeSndConnToDuplex :: s -> ConnId -> RcvQueue -> m ()
|
||||
setRcvQueueStatus :: s -> RcvQueue -> QueueStatus -> m ()
|
||||
setRcvQueueConfirmedE2E :: s -> RcvQueue -> C.PublicKeyX25519 -> C.DhSecretX25519 -> m ()
|
||||
setRcvQueueConfirmedE2E :: s -> RcvQueue -> C.DhSecretX25519 -> m ()
|
||||
setSndQueueStatus :: s -> SndQueue -> QueueStatus -> m ()
|
||||
|
||||
-- Confirmations
|
||||
@@ -85,7 +85,7 @@ data RcvQueue = RcvQueue
|
||||
-- | private DH key related to public sent to sender out-of-band (to agree simple per-queue e2e)
|
||||
e2ePrivKey :: C.PrivateKeyX25519,
|
||||
-- | public sender's DH key and agreed shared DH secret for simple per-queue e2e
|
||||
e2eShared :: Maybe (C.PublicKeyX25519, C.DhSecretX25519),
|
||||
e2eDhSecret :: Maybe C.DhSecretX25519,
|
||||
-- | sender queue ID
|
||||
sndId :: Maybe SMP.SenderId,
|
||||
-- | queue status
|
||||
@@ -100,8 +100,6 @@ data SndQueue = SndQueue
|
||||
sndId :: SMP.SenderId,
|
||||
-- | key used by the sender to sign transmissions
|
||||
sndPrivateKey :: SndPrivateSignKey,
|
||||
-- | public DH key that was (or needs to be) sent to the recipient in SMP confirmation (to agree simple per-queue e2e)
|
||||
e2ePubKey :: C.PublicKeyX25519,
|
||||
-- | shared DH secret agreed for simple per-queue e2e encryption
|
||||
e2eDhSecret :: C.DhSecretX25519,
|
||||
-- | queue status
|
||||
|
||||
@@ -241,20 +241,18 @@ instance (MonadUnliftIO m, MonadError StoreError m) => MonadAgentStore SQLiteSto
|
||||
|]
|
||||
[":status" := status, ":host" := host, ":port" := port, ":rcv_id" := rcvId]
|
||||
|
||||
setRcvQueueConfirmedE2E :: SQLiteStore -> RcvQueue -> C.PublicKeyX25519 -> C.DhSecretX25519 -> m ()
|
||||
setRcvQueueConfirmedE2E st RcvQueue {rcvId, server = SMPServer {host, port}} e2eSndPubKey e2eDhSecret =
|
||||
setRcvQueueConfirmedE2E :: SQLiteStore -> RcvQueue -> C.DhSecretX25519 -> m ()
|
||||
setRcvQueueConfirmedE2E st RcvQueue {rcvId, server = SMPServer {host, port}} e2eDhSecret =
|
||||
liftIO . withTransaction st $ \db ->
|
||||
DB.executeNamed
|
||||
db
|
||||
[sql|
|
||||
UPDATE rcv_queues
|
||||
SET e2e_snd_pub_key = :e2e_snd_pub_key,
|
||||
e2e_dh_secret = :e2e_dh_secret,
|
||||
SET e2e_dh_secret = :e2e_dh_secret,
|
||||
status = :status
|
||||
WHERE host = :host AND port = :port AND rcv_id = :rcv_id
|
||||
|]
|
||||
[ ":status" := Confirmed,
|
||||
":e2e_snd_pub_key" := e2eSndPubKey,
|
||||
":e2e_dh_secret" := e2eDhSecret,
|
||||
":host" := host,
|
||||
":port" := port,
|
||||
@@ -621,15 +619,13 @@ upsertServer_ dbConn SMPServer {host, port, keyHash} = do
|
||||
|
||||
insertRcvQueue_ :: DB.Connection -> ConnId -> RcvQueue -> IO ()
|
||||
insertRcvQueue_ dbConn connId RcvQueue {..} = do
|
||||
let e2eSndPubKey = fst <$> e2eShared :: Maybe C.PublicKeyX25519
|
||||
e2eDhSecret = snd <$> e2eShared :: Maybe C.DhSecretX25519
|
||||
DB.executeNamed
|
||||
dbConn
|
||||
[sql|
|
||||
INSERT INTO rcv_queues
|
||||
( host, port, rcv_id, conn_alias, rcv_private_key, rcv_dh_secret, e2e_priv_key, e2e_snd_pub_key, e2e_dh_secret, snd_id, status)
|
||||
( host, port, rcv_id, conn_alias, rcv_private_key, rcv_dh_secret, e2e_priv_key, e2e_dh_secret, snd_id, status)
|
||||
VALUES
|
||||
(:host,:port,:rcv_id,:conn_alias,:rcv_private_key,:rcv_dh_secret,:e2e_priv_key,:e2e_snd_pub_key,:e2e_dh_secret,:snd_id,:status);
|
||||
(:host,:port,:rcv_id,:conn_alias,:rcv_private_key,:rcv_dh_secret,:e2e_priv_key,:e2e_dh_secret,:snd_id,:status);
|
||||
|]
|
||||
[ ":host" := host server,
|
||||
":port" := port server,
|
||||
@@ -638,7 +634,6 @@ insertRcvQueue_ dbConn connId RcvQueue {..} = do
|
||||
":rcv_private_key" := rcvPrivateKey,
|
||||
":rcv_dh_secret" := rcvDhSecret,
|
||||
":e2e_priv_key" := e2ePrivKey,
|
||||
":e2e_snd_pub_key" := e2eSndPubKey,
|
||||
":e2e_dh_secret" := e2eDhSecret,
|
||||
":snd_id" := sndId,
|
||||
":status" := status
|
||||
@@ -652,16 +647,15 @@ insertSndQueue_ dbConn connId SndQueue {..} = do
|
||||
dbConn
|
||||
[sql|
|
||||
INSERT INTO snd_queues
|
||||
( host, port, snd_id, conn_alias, snd_private_key, e2e_pub_key, e2e_dh_secret, status)
|
||||
( host, port, snd_id, conn_alias, snd_private_key, e2e_dh_secret, status)
|
||||
VALUES
|
||||
(:host,:port,:snd_id,:conn_alias,:snd_private_key,:e2e_pub_key,:e2e_dh_secret,:status);
|
||||
(:host,:port,:snd_id,:conn_alias,:snd_private_key,:e2e_dh_secret,:status);
|
||||
|]
|
||||
[ ":host" := host server,
|
||||
":port" := port server,
|
||||
":snd_id" := sndId,
|
||||
":conn_alias" := connId,
|
||||
":snd_private_key" := sndPrivateKey,
|
||||
":e2e_pub_key" := e2ePubKey,
|
||||
":e2e_dh_secret" := e2eDhSecret,
|
||||
":status" := status
|
||||
]
|
||||
@@ -697,17 +691,16 @@ getRcvQueueByConnAlias_ dbConn connId =
|
||||
dbConn
|
||||
[sql|
|
||||
SELECT s.key_hash, q.host, q.port, q.rcv_id, q.rcv_private_key, q.rcv_dh_secret,
|
||||
q.e2e_priv_key, q.e2e_snd_pub_key, q.e2e_dh_secret, q.snd_id, q.status
|
||||
q.e2e_priv_key, q.e2e_dh_secret, q.snd_id, q.status
|
||||
FROM rcv_queues q
|
||||
INNER JOIN servers s ON q.host = s.host AND q.port = s.port
|
||||
WHERE q.conn_alias = ?;
|
||||
|]
|
||||
(Only connId)
|
||||
where
|
||||
rcvQueue [(keyHash, host, port, rcvId, rcvPrivateKey, rcvDhSecret, e2ePrivKey, e2eSndPubKey, e2eDhSecret, sndId, status)] =
|
||||
rcvQueue [(keyHash, host, port, rcvId, rcvPrivateKey, rcvDhSecret, e2ePrivKey, e2eDhSecret, sndId, status)] =
|
||||
let server = SMPServer host port keyHash
|
||||
e2eShared = (,) <$> e2eSndPubKey <*> e2eDhSecret
|
||||
in Just RcvQueue {server, rcvId, rcvPrivateKey, rcvDhSecret, e2ePrivKey, e2eShared, sndId, status}
|
||||
in Just RcvQueue {server, rcvId, rcvPrivateKey, rcvDhSecret, e2ePrivKey, e2eDhSecret, sndId, status}
|
||||
rcvQueue _ = Nothing
|
||||
|
||||
getSndQueueByConnAlias_ :: DB.Connection -> ConnId -> IO (Maybe SndQueue)
|
||||
@@ -716,17 +709,16 @@ getSndQueueByConnAlias_ dbConn connId =
|
||||
<$> DB.query
|
||||
dbConn
|
||||
[sql|
|
||||
SELECT s.key_hash, q.host, q.port, q.snd_id, q.snd_private_key,
|
||||
q.e2e_pub_key, q.e2e_dh_secret, q.status
|
||||
SELECT s.key_hash, q.host, q.port, q.snd_id, q.snd_private_key, q.e2e_dh_secret, q.status
|
||||
FROM snd_queues q
|
||||
INNER JOIN servers s ON q.host = s.host AND q.port = s.port
|
||||
WHERE q.conn_alias = ?;
|
||||
|]
|
||||
(Only connId)
|
||||
where
|
||||
sndQueue [(keyHash, host, port, sndId, sndPrivateKey, e2ePubKey, e2eDhSecret, status)] =
|
||||
sndQueue [(keyHash, host, port, sndId, sndPrivateKey, e2eDhSecret, status)] =
|
||||
let server = SMPServer host port keyHash
|
||||
in Just SndQueue {server, sndId, sndPrivateKey, e2ePubKey, e2eDhSecret, status}
|
||||
in Just SndQueue {server, sndId, sndPrivateKey, e2eDhSecret, status}
|
||||
sndQueue _ = Nothing
|
||||
|
||||
-- * updateRcvIds helpers
|
||||
|
||||
@@ -815,6 +815,7 @@ cbDecrypt secret (CbNonce nonce) packet
|
||||
tag = Poly1305.auth rs c
|
||||
|
||||
newtype CbNonce = CbNonce {unCbNonce :: ByteString}
|
||||
deriving (Show)
|
||||
|
||||
cbNonce :: ByteString -> CbNonce
|
||||
cbNonce s
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
@@ -66,6 +67,14 @@ instance Encoding ByteString where
|
||||
smpEncode s = B.cons (w2c len) s where len = fromIntegral $ B.length s
|
||||
smpP = A.take . fromIntegral . c2w =<< A.anyChar
|
||||
|
||||
instance Encoding a => Encoding (Maybe a) where
|
||||
smpEncode s = maybe "\0" (("\1" <>) . smpEncode) s
|
||||
smpP =
|
||||
smpP >>= \case
|
||||
'\0' -> pure Nothing
|
||||
'\1' -> Just <$> smpP
|
||||
_ -> fail "invalid Maybe tag"
|
||||
|
||||
newtype Tail = Tail {unTail :: ByteString}
|
||||
|
||||
instance Encoding Tail where
|
||||
|
||||
@@ -322,11 +322,13 @@ data EncMessage = EncMessage
|
||||
emNonce :: C.CbNonce,
|
||||
emBody :: ByteString
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
data PubHeader = PubHeader
|
||||
{ phVersion :: Word16,
|
||||
phE2ePubDhKey :: C.PublicKeyX25519
|
||||
phE2ePubDhKey :: Maybe C.PublicKeyX25519
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
instance Encoding PubHeader where
|
||||
smpEncode (PubHeader v k) = smpEncode (v, k)
|
||||
|
||||
Reference in new issue
Block a user