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:
Evgeny Poberezkin authored and GitHub committed 2022-01-03 14:33:30 +00:00
1 parent 83d085cadc
commit 4a73a7ecd4
10 files changed
+80 -86

No files matched your search

+23 -25
View File
@@ -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
}
+8 -8
View File
@@ -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
+2 -4
View File
@@ -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
+13 -21
View File
@@ -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
+1
View File
@@ -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
+9
View File
@@ -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
+3 -1
View File
@@ -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)