Skip to content

Commit bf28902

Browse files
agent: reuse ratchet on repeat join (#1426)
* agent: reuse ratchet on repeat join * check status * update --------- Co-authored-by: Evgeny Poberezkin <[email protected]>
1 parent 426bf68 commit bf28902

File tree

7 files changed

+92
-22
lines changed

7 files changed

+92
-22
lines changed

simplexmq.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ library
130130
Simplex.Messaging.Agent.Store.SQLite.Migrations.M20240702_servers_stats
131131
Simplex.Messaging.Agent.Store.SQLite.Migrations.M20240930_ntf_tokens_to_delete
132132
Simplex.Messaging.Agent.Store.SQLite.Migrations.M20241007_rcv_queues_last_broker_ts
133+
Simplex.Messaging.Agent.Store.SQLite.Migrations.M20241224_ratchet_e2e_snd_params
133134
Simplex.Messaging.Agent.TRcvQueues
134135
Simplex.Messaging.Client
135136
Simplex.Messaging.Client.Agent

src/Simplex/Messaging/Agent.hs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -834,26 +834,39 @@ joinConn c userId connId enableNtfs cReq cInfo pqSupport subMode = do
834834
startJoinInvitation :: AgentClient -> UserId -> ConnId -> Maybe SndQueue -> Bool -> ConnectionRequestUri 'CMInvitation -> PQSupport -> AM (ConnData, SndQueue, CR.SndE2ERatchetParams 'C.X448)
835835
startJoinInvitation c userId connId sq_ enableNtfs cReqUri pqSup =
836836
lift (compatibleInvitationUri cReqUri) >>= \case
837-
Just (qInfo, Compatible e2eRcvParams@(CR.E2ERatchetParams v _ rcDHRr kem_), Compatible connAgentVersion) -> do
838-
g <- asks random
837+
Just (qInfo, Compatible e2eRcvParams@(CR.E2ERatchetParams v _ _ _), Compatible connAgentVersion) -> do
838+
-- this case avoids re-generating queue keys and subsequent failure of SKEY that timed out
839+
-- e2ePubKey is always present, it's Maybe historically
839840
let pqSupport = pqSup `CR.pqSupportAnd` versionPQSupport_ connAgentVersion (Just v)
841+
(sq', e2eSndParams) <- case sq_ of
842+
Just sq@SndQueue {e2ePubKey = Just _k} -> do
843+
e2eSndParams <-
844+
withStore' c (\db -> getSndRatchet db connId v) >>= \case
845+
Right r -> pure $ snd r
846+
Left e -> do
847+
atomically $ writeTBQueue (subQ c) ("", connId, AEvt SAEConn (ERR $ INTERNAL $ "no snd ratchet " <> show e))
848+
createRatchet_ pqSupport e2eRcvParams
849+
pure (sq, e2eSndParams)
850+
_ -> do
851+
q <- lift $ fst <$> newSndQueue userId "" qInfo
852+
e2eSndParams <- createRatchet_ pqSupport e2eRcvParams
853+
withStore c $ \db -> runExceptT $ do
854+
sq' <- maybe (ExceptT $ updateNewConnSnd db connId q) pure sq_
855+
pure (sq', e2eSndParams)
856+
let cData = ConnData {userId, connId, connAgentVersion, enableNtfs, lastExternalSndId = 0, deleted = False, ratchetSyncState = RSOk, pqSupport}
857+
pure (cData, sq', e2eSndParams)
858+
Nothing -> throwE $ AGENT A_VERSION
859+
where
860+
createRatchet_ pqSupport e2eRcvParams@(CR.E2ERatchetParams v _ rcDHRr kem_) = do
861+
g <- asks random
840862
(pk1, pk2, pKem, e2eSndParams) <- liftIO $ CR.generateSndE2EParams g v (CR.replyKEM_ v kem_ pqSupport)
841863
(_, rcDHRs) <- atomically $ C.generateKeyPair g
842864
rcParams <- liftEitherWith cryptoError $ CR.pqX3dhSnd pk1 pk2 pKem e2eRcvParams
843865
maxSupported <- asks $ maxVersion . e2eEncryptVRange . config
844866
let rcVs = CR.RatchetVersions {current = v, maxSupported}
845867
rc = CR.initSndRatchet rcVs rcDHRr rcDHRs rcParams
846-
-- this case avoids re-generating queue keys and subsequent failure of SKEY that timed out
847-
-- e2ePubKey is always present, it's Maybe historically
848-
q <- case sq_ of
849-
Just sq@SndQueue {e2ePubKey = Just _k} -> pure (sq :: SndQueue) {dbQueueId = DBNewQueue}
850-
_ -> lift $ fst <$> newSndQueue userId "" qInfo
851-
let cData = ConnData {userId, connId, connAgentVersion, enableNtfs, lastExternalSndId = 0, deleted = False, ratchetSyncState = RSOk, pqSupport}
852-
sq' <- withStore c $ \db -> runExceptT $ do
853-
liftIO $ createRatchet db connId rc
854-
maybe (ExceptT $ updateNewConnSnd db connId q) pure sq_
855-
pure (cData, sq', e2eSndParams)
856-
Nothing -> throwE $ AGENT A_VERSION
868+
withStore' c $ \db -> createSndRatchet db connId rc e2eSndParams
869+
pure e2eSndParams
857870

858871
connRequestPQSupport :: AgentClient -> PQSupport -> ConnectionRequestUri c -> IO (Maybe (VersionSMPA, PQSupport))
859872
connRequestPQSupport c pqSup cReq = withAgentEnv' c $ case cReq of
@@ -892,6 +905,7 @@ joinConnSrv c userId connId enableNtfs inv@CRInvitationUri {} cInfo pqSup subMod
892905
case conn of
893906
NewConnection _ -> doJoin Nothing
894907
SndConnection _ sq -> doJoin $ Just sq
908+
DuplexConnection _ (RcvQueue {status = New} :| _) (sq@SndQueue {status = New} :| _) -> doJoin $ Just sq
895909
_ -> throwE $ CMD PROHIBITED $ "joinConnSrv: bad connection " <> show cType
896910
where
897911
doJoin :: Maybe SndQueue -> AM SndQueueSecured

src/Simplex/Messaging/Agent/Store/SQLite.hs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ module Simplex.Messaging.Agent.Store.SQLite
131131
-- Double ratchet persistence
132132
createRatchetX3dhKeys,
133133
getRatchetX3dhKeys,
134+
createSndRatchet,
135+
getSndRatchet,
134136
setRatchetX3dhKeys,
135137
createRatchet,
136138
deleteRatchet,
@@ -245,7 +247,6 @@ where
245247

246248
import Control.Logger.Simple
247249
import Control.Monad
248-
import Control.Monad.Except
249250
import Control.Monad.IO.Class
250251
import Control.Monad.Trans.Except
251252
import Crypto.Random (ChaChaDRG)
@@ -586,7 +587,6 @@ updateNewConnSnd :: DB.Connection -> ConnId -> NewSndQueue -> IO (Either StoreEr
586587
updateNewConnSnd db connId sq =
587588
getConn db connId $>>= \case
588589
(SomeConn _ NewConnection {}) -> updateConn
589-
(SomeConn _ SndConnection {}) -> updateConn -- to allow retries
590590
(SomeConn c _) -> pure . Left . SEBadConnType $ connType c
591591
where
592592
updateConn :: IO (Either StoreError SndQueue)
@@ -1250,19 +1250,48 @@ getRatchetX3dhKeys db connId =
12501250
(Just k1, Just k2, pKem) -> Right (k1, k2, pKem)
12511251
_ -> Left SEX3dhKeysNotFound
12521252

1253+
createSndRatchet :: DB.Connection -> ConnId -> RatchetX448 -> CR.AE2ERatchetParams 'C.X448 -> IO ()
1254+
createSndRatchet db connId ratchetState (CR.AE2ERatchetParams s (CR.E2ERatchetParams _ x3dhPubKey1 x3dhPubKey2 pqPubKem)) =
1255+
DB.execute
1256+
db
1257+
[sql|
1258+
INSERT INTO ratchets
1259+
(conn_id, ratchet_state, x3dh_pub_key_1, x3dh_pub_key_2, pq_pub_kem) VALUES (?, ?, ?, ?, ?)
1260+
ON CONFLICT (conn_id) DO UPDATE SET
1261+
ratchet_state = EXCLUDED.ratchet_state,
1262+
x3dh_priv_key_1 = NULL,
1263+
x3dh_priv_key_2 = NULL,
1264+
x3dh_pub_key_1 = EXCLUDED.x3dh_pub_key_1,
1265+
x3dh_pub_key_2 = EXCLUDED.x3dh_pub_key_2,
1266+
pq_priv_kem = NULL,
1267+
pq_pub_kem = EXCLUDED.pq_pub_kem
1268+
|]
1269+
(connId, ratchetState, x3dhPubKey1, x3dhPubKey2, CR.ARKP s <$> pqPubKem)
1270+
1271+
getSndRatchet :: DB.Connection -> ConnId -> CR.VersionE2E -> IO (Either StoreError (RatchetX448, CR.AE2ERatchetParams 'C.X448))
1272+
getSndRatchet db connId v =
1273+
firstRow' result SEX3dhKeysNotFound $
1274+
DB.query db "SELECT ratchet_state, x3dh_pub_key_1, x3dh_pub_key_2, pq_pub_kem FROM ratchets WHERE conn_id = ?" (Only connId)
1275+
where
1276+
result = \case
1277+
(Just ratchetState, Just k1, Just k2, pKem_) ->
1278+
let params = case pKem_ of
1279+
Nothing -> CR.AE2ERatchetParams CR.SRKSProposed (CR.E2ERatchetParams v k1 k2 Nothing)
1280+
Just (CR.ARKP s pKem) -> CR.AE2ERatchetParams s (CR.E2ERatchetParams v k1 k2 (Just pKem))
1281+
in Right (ratchetState, params)
1282+
_ -> Left SEX3dhKeysNotFound
1283+
12531284
-- used to remember new keys when starting ratchet re-synchronization
1254-
-- TODO remove the columns for public keys in v5.7.
1255-
-- Currently, the keys are not used but still stored to support app downgrade to the previous version.
12561285
setRatchetX3dhKeys :: DB.Connection -> ConnId -> C.PrivateKeyX448 -> C.PrivateKeyX448 -> Maybe CR.RcvPrivRKEMParams -> IO ()
12571286
setRatchetX3dhKeys db connId x3dhPrivKey1 x3dhPrivKey2 pqPrivKem =
12581287
DB.execute
12591288
db
12601289
[sql|
12611290
UPDATE ratchets
1262-
SET x3dh_priv_key_1 = ?, x3dh_priv_key_2 = ?, x3dh_pub_key_1 = ?, x3dh_pub_key_2 = ?, pq_priv_kem = ?
1291+
SET x3dh_priv_key_1 = ?, x3dh_priv_key_2 = ?, pq_priv_kem = ?
12631292
WHERE conn_id = ?
12641293
|]
1265-
(x3dhPrivKey1, x3dhPrivKey2, C.publicKey x3dhPrivKey1, C.publicKey x3dhPrivKey2, pqPrivKem, connId)
1294+
(x3dhPrivKey1, x3dhPrivKey2, pqPrivKem, connId)
12661295

12671296
-- TODO remove the columns for public keys in v5.7.
12681297
createRatchet :: DB.Connection -> ConnId -> RatchetX448 -> IO ()
@@ -1278,7 +1307,8 @@ createRatchet db connId rc =
12781307
x3dh_priv_key_2 = NULL,
12791308
x3dh_pub_key_1 = NULL,
12801309
x3dh_pub_key_2 = NULL,
1281-
pq_priv_kem = NULL
1310+
pq_priv_kem = NULL,
1311+
pq_pub_kem = NULL
12821312
|]
12831313
[":conn_id" := connId, ":ratchet_state" := rc]
12841314

src/Simplex/Messaging/Agent/Store/SQLite/Migrations.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import Simplex.Messaging.Agent.Store.SQLite.Migrations.M20240624_snd_secure
7676
import Simplex.Messaging.Agent.Store.SQLite.Migrations.M20240702_servers_stats
7777
import Simplex.Messaging.Agent.Store.SQLite.Migrations.M20240930_ntf_tokens_to_delete
7878
import Simplex.Messaging.Agent.Store.SQLite.Migrations.M20241007_rcv_queues_last_broker_ts
79+
import Simplex.Messaging.Agent.Store.SQLite.Migrations.M20241224_ratchet_e2e_snd_params
7980
import Simplex.Messaging.Encoding.String
8081
import Simplex.Messaging.Parsers (dropPrefix, sumTypeJSON)
8182
import Simplex.Messaging.Transport.Client (TransportHost)
@@ -120,7 +121,8 @@ schemaMigrations =
120121
("m20240624_snd_secure", m20240624_snd_secure, Just down_m20240624_snd_secure),
121122
("m20240702_servers_stats", m20240702_servers_stats, Just down_m20240702_servers_stats),
122123
("m20240930_ntf_tokens_to_delete", m20240930_ntf_tokens_to_delete, Just down_m20240930_ntf_tokens_to_delete),
123-
("m20241007_rcv_queues_last_broker_ts", m20241007_rcv_queues_last_broker_ts, Just down_m20241007_rcv_queues_last_broker_ts)
124+
("m20241007_rcv_queues_last_broker_ts", m20241007_rcv_queues_last_broker_ts, Just down_m20241007_rcv_queues_last_broker_ts),
125+
("m20241224_ratchet_e2e_snd_params", m20241224_ratchet_e2e_snd_params, Just down_m20241224_ratchet_e2e_snd_params)
124126
]
125127

126128
-- | The list of migrations in ascending order by date
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{-# LANGUAGE QuasiQuotes #-}
2+
3+
module Simplex.Messaging.Agent.Store.SQLite.Migrations.M20241224_ratchet_e2e_snd_params where
4+
5+
import Database.SQLite.Simple (Query)
6+
import Database.SQLite.Simple.QQ (sql)
7+
8+
m20241224_ratchet_e2e_snd_params :: Query
9+
m20241224_ratchet_e2e_snd_params =
10+
[sql|
11+
ALTER TABLE ratchets ADD COLUMN pq_pub_kem BLOB;
12+
|]
13+
14+
down_m20241224_ratchet_e2e_snd_params :: Query
15+
down_m20241224_ratchet_e2e_snd_params =
16+
[sql|
17+
ALTER TABLE ratchets DROP COLUMN pq_pub_kem;
18+
|]

src/Simplex/Messaging/Agent/Store/SQLite/Migrations/agent_schema.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@ CREATE TABLE ratchets(
166166
,
167167
x3dh_pub_key_1 BLOB,
168168
x3dh_pub_key_2 BLOB,
169-
pq_priv_kem BLOB
169+
pq_priv_kem BLOB,
170+
pq_pub_kem BLOB
170171
) WITHOUT ROWID;
171172
CREATE TABLE skipped_messages(
172173
skipped_message_id INTEGER PRIMARY KEY,

src/Simplex/Messaging/Crypto/Ratchet.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ instance Encoding ARKEMParams where
206206
'A' -> ARKP SRKSAccepted .: RKParamsAccepted <$> smpP <*> smpP
207207
_ -> fail "bad ratchet KEM params"
208208

209+
instance ToField ARKEMParams where toField = toField . smpEncode
210+
211+
instance FromField ARKEMParams where fromField = blobFieldDecoder smpDecode
212+
209213
data E2ERatchetParams (s :: RatchetKEMState) (a :: Algorithm)
210214
= E2ERatchetParams VersionE2E (PublicKey a) (PublicKey a) (Maybe (RKEMParams s))
211215
deriving (Show)

0 commit comments

Comments
 (0)