@@ -48,6 +48,7 @@ module Simplex.Messaging.Agent.Client
48
48
newRcvQueue ,
49
49
newRcvQueue_ ,
50
50
subscribeQueues ,
51
+ subscribeClientService ,
51
52
getQueueMessage ,
52
53
decryptSMPMessage ,
53
54
addSubscription ,
@@ -215,6 +216,7 @@ import Data.Text.Encoding
215
216
import Data.Time (UTCTime , addUTCTime , defaultTimeLocale , formatTime , getCurrentTime )
216
217
import Data.Time.Clock.System (getSystemTime )
217
218
import Data.Word (Word16 )
219
+ import qualified Data.X509.Validation as XV
218
220
import Network.Socket (HostName )
219
221
import Simplex.FileTransfer.Client (XFTPChunkSpec (.. ), XFTPClient , XFTPClientConfig (.. ), XFTPClientError )
220
222
import qualified Simplex.FileTransfer.Client as X
@@ -230,7 +232,8 @@ import Simplex.Messaging.Agent.Protocol
230
232
import Simplex.Messaging.Agent.RetryInterval
231
233
import Simplex.Messaging.Agent.Stats
232
234
import Simplex.Messaging.Agent.Store
233
- import Simplex.Messaging.Agent.Store.Common (DBStore , withTransaction )
235
+ import Simplex.Messaging.Agent.Store.AgentStore
236
+ import Simplex.Messaging.Agent.Store.Common (DBStore )
234
237
import qualified Simplex.Messaging.Agent.Store.DB as DB
235
238
import Simplex.Messaging.Agent.TRcvQueues (TRcvQueues (getRcvQueues ))
236
239
import qualified Simplex.Messaging.Agent.TRcvQueues as RQ
@@ -284,8 +287,9 @@ import Simplex.Messaging.Session
284
287
import Simplex.Messaging.Agent.Store.Entity
285
288
import Simplex.Messaging.TMap (TMap )
286
289
import qualified Simplex.Messaging.TMap as TM
287
- import Simplex.Messaging.Transport (SMPVersion , ServiceCredentials , SessionId , THandleParams (sessionId , thVersion ), TransportError (.. ), TransportPeer (.. ), sndAuthKeySMPVersion , shortLinksSMPVersion , newNtfCredsSMPVersion )
290
+ import Simplex.Messaging.Transport (SMPServiceRole ( .. ), SMPVersion , ServiceCredentials ( .. ) , SessionId , THClientService' ( .. ) , THandleParams (sessionId , thVersion ), TransportError (.. ), TransportPeer (.. ), sndAuthKeySMPVersion , shortLinksSMPVersion , newNtfCredsSMPVersion )
288
291
import Simplex.Messaging.Transport.Client (TransportHost (.. ))
292
+ import Simplex.Messaging.Transport.Credentials
289
293
import Simplex.Messaging.Util
290
294
import Simplex.Messaging.Version
291
295
import System.Mem.Weak (Weak , deRefWeak )
@@ -321,7 +325,7 @@ data AgentClient = AgentClient
321
325
msgQ :: TBQueue (ServerTransmissionBatch SMPVersion ErrorType BrokerMsg ),
322
326
smpServers :: TMap UserId (UserServers 'PSMP),
323
327
smpClients :: TMap SMPTransportSession SMPClientVar ,
324
- smpServiceCreds :: TMap UserId ( Maybe ( TMap SMPServer ServiceCredentials )), -- Nothing means not to use certificates for this user record
328
+ useClientServices :: TMap UserId Bool ,
325
329
-- smpProxiedRelays:
326
330
-- SMPTransportSession defines connection from proxy to relay,
327
331
-- SMPServerWithAuth defines client connected to SMP proxy (with the same userId and entityId in TransportSession)
@@ -494,7 +498,7 @@ newAgentClient clientId InitialAgentServers {smp, ntf, xftp, netCfg, useServices
494
498
msgQ <- newTBQueueIO qSize
495
499
smpServers <- newTVarIO $ M. map mkUserServers smp
496
500
smpClients <- TM. emptyIO
497
- smpServiceCreds <- newTVarIO =<< mapM ( \ enable -> if enable then Just <$> TM. emptyIO else pure Nothing ) useServices
501
+ useClientServices <- newTVarIO useServices
498
502
smpProxiedRelays <- TM. emptyIO
499
503
ntfServers <- newTVarIO ntf
500
504
ntfClients <- TM. emptyIO
@@ -533,7 +537,7 @@ newAgentClient clientId InitialAgentServers {smp, ntf, xftp, netCfg, useServices
533
537
msgQ,
534
538
smpServers,
535
539
smpClients,
536
- smpServiceCreds ,
540
+ useClientServices ,
537
541
smpProxiedRelays,
538
542
ntfServers,
539
543
ntfClients,
@@ -586,6 +590,28 @@ agentDRG :: AgentClient -> TVar ChaChaDRG
586
590
agentDRG AgentClient {agentEnv = Env {random}} = random
587
591
{-# INLINE agentDRG #-}
588
592
593
+ getServiceCredentials :: AgentClient -> UserId -> SMPServer -> AM (Maybe (ServiceCredentials , Maybe ServiceId ))
594
+ getServiceCredentials c userId srv =
595
+ liftIO (TM. lookupIO userId $ useClientServices c)
596
+ $>>= \ useService -> if useService then Just <$> getService else pure Nothing
597
+ where
598
+ getService :: AM (ServiceCredentials , Maybe ServiceId )
599
+ getService = do
600
+ let g = agentDRG c
601
+ ((C. KeyHash kh, serviceCreds), serviceId_) <-
602
+ withStore' c $ \ db ->
603
+ getClientService db userId srv >>= \ case
604
+ Just service -> pure service
605
+ Nothing -> do
606
+ cred <- genCredentials g Nothing (25 , 24 * 999999 ) " simplex"
607
+ let tlsCreds = tlsCredentials [cred]
608
+ createClientService db userId srv tlsCreds
609
+ pure (tlsCreds, Nothing )
610
+ (_, pk) <- atomically $ C. generateKeyPair g
611
+ let serviceSignKey = C. APrivateSignKey C. SEd25519 pk
612
+ creds = ServiceCredentials {serviceRole = SRMessaging , serviceCreds, serviceCertHash = XV. Fingerprint kh, serviceSignKey}
613
+ pure (creds, serviceId_)
614
+
589
615
class (Encoding err , Show err ) => ProtocolServerClient v err msg | msg -> v , msg -> err where
590
616
type Client msg = c | c -> msg
591
617
getProtocolServerClient :: AgentClient -> NetworkRequestMode -> TransportSession msg -> AM (Client msg )
@@ -689,19 +715,29 @@ getSMPProxyClient c@AgentClient {active, smpClients, smpProxiedRelays, workerSeq
689
715
Nothing -> Left $ BROKER (B. unpack $ strEncode srv) TIMEOUT
690
716
691
717
smpConnectClient :: AgentClient -> NetworkRequestMode -> SMPTransportSession -> TMap SMPServer ProxiedRelayVar -> SMPClientVar -> AM SMPConnectedClient
692
- smpConnectClient c@ AgentClient {smpClients, msgQ, proxySessTs} nm tSess@ (_ , srv, _) prs v =
718
+ smpConnectClient c@ AgentClient {smpClients, msgQ, proxySessTs} nm tSess@ (userId , srv, _) prs v =
693
719
newProtocolClient c tSess smpClients connectClient v
694
720
`catchAgentError` \ e -> lift (resubscribeSMPSession c tSess) >> throwE e
695
721
where
696
722
connectClient :: SMPClientVar -> AM SMPConnectedClient
697
723
connectClient v' = do
698
724
cfg <- lift $ getClientConfig c smpCfg
699
725
g <- asks random
726
+ service <- getServiceCredentials c userId srv
727
+ let cfg' = cfg {serviceCredentials = fst <$> service}
700
728
env <- ask
701
- liftError (protocolClientError SMP $ B. unpack $ strEncode srv) $ do
729
+ smp <- liftError (protocolClientError SMP $ B. unpack $ strEncode srv) $ do
702
730
ts <- readTVarIO proxySessTs
703
- smp <- ExceptT $ getProtocolClient g nm tSess cfg (presetSMPDomains c) (Just msgQ) ts $ smpClientDisconnected c tSess env v' prs
704
- pure SMPConnectedClient {connectedClient = smp, proxiedRelays = prs}
731
+ ExceptT $ getProtocolClient g nm tSess cfg' (presetSMPDomains c) (Just msgQ) ts $ smpClientDisconnected c tSess env v' prs
732
+ updateClientService service smp
733
+ pure SMPConnectedClient {connectedClient = smp, proxiedRelays = prs}
734
+ updateClientService service smp = case (service, smpClientService smp) of
735
+ (Just (_, serviceId_), Just THClientService {serviceId})
736
+ | serviceId_ /= Just serviceId -> withStore' c $ \ db -> setClientServiceId db userId srv serviceId
737
+ | otherwise -> pure ()
738
+ (Just _, Nothing ) -> withStore' c $ \ db -> deleteClientService db userId srv -- e.g., server version downgrade
739
+ (Nothing , Just _) -> logError " server returned serviceId without service credentials in request"
740
+ (Nothing , Nothing ) -> pure ()
705
741
706
742
smpClientDisconnected :: AgentClient -> SMPTransportSession -> Env -> SMPClientVar -> TMap SMPServer ProxiedRelayVar -> SMPClient -> IO ()
707
743
smpClientDisconnected c@ AgentClient {active, smpClients, smpProxiedRelays} tSess@ (userId, srv, qId) env v prs client = do
@@ -858,7 +894,6 @@ waitForProtocolClient c nm tSess@(_, srv, _) clients v = do
858
894
(throwE e)
859
895
Nothing -> throwE $ BROKER (B. unpack $ strEncode srv) TIMEOUT
860
896
861
- -- clientConnected arg is only passed for SMP server
862
897
newProtocolClient ::
863
898
forall v err msg .
864
899
(ProtocolTypeI (ProtoType msg ), ProtocolServerClient v err msg ) =>
@@ -1355,7 +1390,7 @@ getSessionMode :: MonadIO m => AgentClient -> m TransportSessionMode
1355
1390
getSessionMode = fmap sessionMode . getNetworkConfig
1356
1391
{-# INLINE getSessionMode #-}
1357
1392
1358
- newRcvQueue :: AgentClient -> NetworkRequestMode -> UserId -> ConnId -> SMPServerWithAuth -> VersionRangeSMPC -> SConnectionMode c -> Bool -> SubscriptionMode -> AM (NewRcvQueue , SMPQueueUri , Maybe ServiceId , SMPTransportSession , SessionId )
1393
+ newRcvQueue :: AgentClient -> NetworkRequestMode -> UserId -> ConnId -> SMPServerWithAuth -> VersionRangeSMPC -> SConnectionMode c -> Bool -> SubscriptionMode -> AM (NewRcvQueue , SMPQueueUri , SMPTransportSession , SessionId )
1359
1394
newRcvQueue c nm userId connId srv vRange cMode enableNtfs subMode = do
1360
1395
let qrd = case cMode of SCMInvitation -> CQRMessaging Nothing ; SCMContact -> CQRContact Nothing
1361
1396
e2eKeys <- atomically . C. generateKeyPair =<< asks random
@@ -1376,7 +1411,7 @@ queueReqData = \case
1376
1411
CQRMessaging d -> QRMessaging $ srvReq <$> d
1377
1412
CQRContact d -> QRContact $ srvReq <$> d
1378
1413
1379
- newRcvQueue_ :: AgentClient -> NetworkRequestMode -> UserId -> ConnId -> SMPServerWithAuth -> VersionRangeSMPC -> ClntQueueReqData -> Bool -> SubscriptionMode -> Maybe C. CbNonce -> C. KeyPairX25519 -> AM (NewRcvQueue , SMPQueueUri , Maybe ServiceId , SMPTransportSession , SessionId )
1414
+ newRcvQueue_ :: AgentClient -> NetworkRequestMode -> UserId -> ConnId -> SMPServerWithAuth -> VersionRangeSMPC -> ClntQueueReqData -> Bool -> SubscriptionMode -> Maybe C. CbNonce -> C. KeyPairX25519 -> AM (NewRcvQueue , SMPQueueUri , SMPTransportSession , SessionId )
1380
1415
newRcvQueue_ c nm userId connId (ProtoServerWithAuth srv auth) vRange cqrd enableNtfs subMode nonce_ (e2eDhKey, e2ePrivKey) = do
1381
1416
C. AuthAlg a <- asks (rcvAuthAlg . config)
1382
1417
g <- asks random
@@ -1388,7 +1423,7 @@ newRcvQueue_ c nm userId connId (ProtoServerWithAuth srv auth) vRange cqrd enabl
1388
1423
withClient c nm tSess $ \ (SMPConnectedClient smp _) -> do
1389
1424
(ntfKeys, ntfCreds) <- liftIO $ mkNtfCreds a g smp
1390
1425
(thParams smp,ntfKeys,) <$> createSMPQueue smp nm nonce_ rKeys dhKey auth subMode (queueReqData cqrd) ntfCreds
1391
- -- TODO [certs rcv] validate that serviceId is the same as in the client session
1426
+ -- TODO [certs rcv] validate that serviceId is the same as in the client session, fail otherwise
1392
1427
liftIO . logServer " <--" c srv NoEntity $ B. unwords [" IDS" , logSecret rcvId, logSecret sndId]
1393
1428
shortLink <- mkShortLinkCreds thParams' qik
1394
1429
let rq =
@@ -1415,7 +1450,7 @@ newRcvQueue_ c nm userId connId (ProtoServerWithAuth srv auth) vRange cqrd enabl
1415
1450
deleteErrors = 0
1416
1451
}
1417
1452
qUri = SMPQueueUri vRange $ SMPQueueAddress srv sndId e2eDhKey queueMode
1418
- pure (rq, qUri, serviceId, tSess, sessionId thParams')
1453
+ pure (rq, qUri, tSess, sessionId thParams')
1419
1454
where
1420
1455
mkNtfCreds :: (C. AlgorithmI a , C. AuthAlgorithm a ) => C. SAlgorithm a -> TVar ChaChaDRG -> SMPClient -> IO (Maybe (C. AAuthKeyPair , C. PrivateKeyX25519 ), Maybe NewNtfCreds )
1421
1456
mkNtfCreds a g smp
@@ -1540,6 +1575,11 @@ subscribeQueues c qs = do
1540
1575
processSubResults = mapM_ $ uncurry $ processSubResult c sessId
1541
1576
resubscribe = resubscribeSMPSession c tSess `runReaderT` env
1542
1577
1578
+ subscribeClientService :: AgentClient -> UserId -> SMPServer -> AM Int64
1579
+ subscribeClientService c userId srv =
1580
+ withLogClient c NRMBackground (userId, srv, Nothing ) B. empty " SUBS" $
1581
+ (`subscribeService` SMP. SRecipientService ) . connectedClient
1582
+
1543
1583
activeClientSession :: AgentClient -> SMPTransportSession -> SessionId -> STM Bool
1544
1584
activeClientSession c tSess sessId = sameSess <$> tryReadSessVar tSess (smpClients c)
1545
1585
where
0 commit comments