Skip to content

Commit 6092fc4

Browse files
committed
test: delivery to client with service certificate
1 parent 56d9170 commit 6092fc4

File tree

6 files changed

+105
-11
lines changed

6 files changed

+105
-11
lines changed

simplexmq.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ test-suite simplexmq-test
543543
, text
544544
, time
545545
, timeit ==2.0.*
546+
, tls >=1.9.0 && <1.10
546547
, transformers
547548
, unliftio
548549
, unliftio-core

src/Simplex/Messaging/Server.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1488,7 +1488,7 @@ client
14881488
QUE -> withQueue $ \q qr -> (corrId,entId,) <$> getQueueInfo q qr
14891489
Cmd SRecipientService SUBS -> response . (corrId,entId,) <$> case clntServiceId of
14901490
Just serviceId -> subscribeServiceMessages serviceId
1491-
Nothing -> pure $ ERR INTERNAL
1491+
Nothing -> pure $ ERR INTERNAL -- it's "internal" because it should never get to this branch
14921492
where
14931493
createQueue :: NewQueueReq -> M s (Transmission BrokerMsg)
14941494
createQueue NewQueueReq {rcvAuthKey, rcvDhKey, subMode, queueReqData, ntfCreds}

src/Simplex/Messaging/Server/QueueStore/STM.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ instance StoreQueueClass q => QueueStoreClass q (STMQueueStore q) where
115115
serviceQueuesCount serviceSel = foldM (\n s -> (n +) . S.size <$> readTVarIO (serviceSel s)) 0
116116

117117
addQueue_ :: STMQueueStore q -> (RecipientId -> QueueRec -> IO q) -> RecipientId -> QueueRec -> IO (Either ErrorType q)
118-
addQueue_ st mkQ rId qr@QueueRec {senderId = sId, notifier, queueData} = do
118+
addQueue_ st mkQ rId qr@QueueRec {senderId = sId, notifier, queueData, rcvServiceId} = do
119119
sq <- mkQ rId qr
120120
add sq $>> withLog "addStoreQueue" st (\s -> logCreateQueue s rId qr) $> Right sq
121121
where
@@ -125,6 +125,7 @@ instance StoreQueueClass q => QueueStoreClass q (STMQueueStore q) where
125125
TM.insert sId rId senders
126126
forM_ notifier $ \NtfCreds {notifierId} -> TM.insert notifierId rId notifiers
127127
forM_ queueData $ \(lnkId, _) -> TM.insert lnkId rId links
128+
mapM_ (addServiceQueue st serviceRcvQueues rId) rcvServiceId
128129
hasId = anyM [TM.member rId queues, TM.member sId senders, hasNotifier, hasLink]
129130
hasNotifier = maybe (pure False) (\NtfCreds {notifierId} -> TM.member notifierId notifiers) notifier
130131
hasLink = maybe (pure False) (\(lnkId, _) -> TM.member lnkId links) queueData

tests/SMPClient.hs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@
1515

1616
module SMPClient where
1717

18+
import Control.Monad
1819
import Control.Monad.Except (runExceptT)
1920
import Data.ByteString.Char8 (ByteString)
2021
import Data.List.NonEmpty (NonEmpty)
22+
import qualified Data.X509 as X
23+
import qualified Data.X509.Validation as XV
2124
import Network.Socket
25+
import qualified Network.TLS as TLS
2226
import Simplex.Messaging.Agent.Store.Postgres.Options (DBOpts (..))
2327
import Simplex.Messaging.Agent.Store.Shared (MigrationConfirmation (..))
2428
import Simplex.Messaging.Client (ProtocolClientConfig (..), chooseTransportHost, defaultNetworkConfig)
@@ -33,6 +37,7 @@ import Simplex.Messaging.Server.QueueStore.Postgres.Config (PostgresStoreCfg (..
3337
import Simplex.Messaging.Transport
3438
import Simplex.Messaging.Transport.Client
3539
import Simplex.Messaging.Transport.Server
40+
import Simplex.Messaging.Transport.Shared (ChainCertificates (..), chainIdCaCerts)
3641
import Simplex.Messaging.Util (ifM)
3742
import Simplex.Messaging.Version
3843
import Simplex.Messaging.Version.Internal
@@ -151,13 +156,26 @@ testSMPClient = testSMPClientVR supportedClientSMPRelayVRange
151156
testSMPClientVR :: Transport c => VersionRangeSMP -> (THandleSMP c 'TClient -> IO a) -> IO a
152157
testSMPClientVR vr client = do
153158
Right useHost <- pure $ chooseTransportHost defaultNetworkConfig testHost
154-
testSMPClient_ useHost testPort vr client
159+
testSMPClient_ useHost testPort vr Nothing client
155160

156-
testSMPClient_ :: Transport c => TransportHost -> ServiceName -> VersionRangeSMP -> (THandleSMP c 'TClient -> IO a) -> IO a
157-
testSMPClient_ host port vr client = do
158-
let tcConfig = defaultTransportClientConfig {clientALPN} :: TransportClientConfig
161+
testSMPServiceClient :: Transport c => (TLS.Credential, C.KeyPairEd25519) -> (THandleSMP c 'TClient -> IO a) -> IO a
162+
testSMPServiceClient serviceCreds client = do
163+
Right useHost <- pure $ chooseTransportHost defaultNetworkConfig testHost
164+
testSMPClient_ useHost testPort supportedClientSMPRelayVRange (Just serviceCreds) client
165+
166+
testSMPClient_ :: Transport c => TransportHost -> ServiceName -> VersionRangeSMP -> Maybe (TLS.Credential, C.KeyPairEd25519) -> (THandleSMP c 'TClient -> IO a) -> IO a
167+
testSMPClient_ host port vr serviceCreds_ client = do
168+
serviceAndKeys_ <- forM serviceCreds_ $ \(serviceCreds@(cc, pk), keys) -> do
169+
Right serviceSignKey <- pure $ C.x509ToPrivate' pk
170+
let idCert' = case chainIdCaCerts cc of
171+
CCSelf cert -> cert
172+
CCValid {idCert} -> idCert
173+
_ -> error "bad certificate"
174+
serviceCertHash = XV.getFingerprint idCert' X.HashSHA256
175+
pure (ServiceCredentials {serviceRole = SRMessaging, serviceCreds, serviceCertHash, serviceSignKey}, keys)
176+
let tcConfig = defaultTransportClientConfig {clientALPN, clientCredentials = fst <$> serviceCreds_} :: TransportClientConfig
159177
runTransportClient tcConfig Nothing host port (Just testKeyHash) $ \h ->
160-
runExceptT (smpClientHandshake h Nothing testKeyHash vr False Nothing) >>= \case
178+
runExceptT (smpClientHandshake h Nothing testKeyHash vr False serviceAndKeys_) >>= \case
161179
Right th -> client th
162180
Left e -> error $ show e
163181
where

tests/SMPProxyTests.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,14 +434,14 @@ agentViaProxyRetryNoSession = do
434434
testNoProxy :: AStoreType -> IO ()
435435
testNoProxy msType = do
436436
withSmpServerConfigOn (transport @TLS) (cfgMS msType) testPort2 $ \_ -> do
437-
testSMPClient_ "127.0.0.1" testPort2 proxyVRangeV8 $ \(th :: THandleSMP TLS 'TClient) -> do
437+
testSMPClient_ "127.0.0.1" testPort2 proxyVRangeV8 Nothing $ \(th :: THandleSMP TLS 'TClient) -> do
438438
(_, _, reply) <- sendRecv th (Nothing, "0", NoEntity, SMP.PRXY testSMPServer Nothing)
439439
reply `shouldBe` Right (SMP.ERR $ SMP.PROXY SMP.BASIC_AUTH)
440440

441441
testProxyAuth :: AStoreType -> IO ()
442442
testProxyAuth msType = do
443443
withSmpServerConfigOn (transport @TLS) proxyCfgAuth testPort $ \_ -> do
444-
testSMPClient_ "127.0.0.1" testPort proxyVRangeV8 $ \(th :: THandleSMP TLS 'TClient) -> do
444+
testSMPClient_ "127.0.0.1" testPort proxyVRangeV8 Nothing $ \(th :: THandleSMP TLS 'TClient) -> do
445445
(_, _, reply) <- sendRecv th (Nothing, "0", NoEntity, SMP.PRXY testSMPServer2 $ Just "wrong")
446446
reply `shouldBe` Right (SMP.ERR $ SMP.PROXY SMP.BASIC_AUTH)
447447
where

tests/ServerTests.hs

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import Data.String (IsString (..))
3535
import Data.Type.Equality
3636
import qualified Data.X509.Validation as XV
3737
import GHC.Stack (withFrozenCallStack)
38+
import qualified Network.TLS as TLS
3839
import SMPClient
3940
import qualified Simplex.Messaging.Crypto as C
4041
import Simplex.Messaging.Encoding
@@ -49,6 +50,7 @@ import Simplex.Messaging.Server.MsgStore.Types (MsgStoreClass (..), SMSType (..)
4950
import Simplex.Messaging.Server.Stats (PeriodStatsData (..), ServerStatsData (..))
5051
import Simplex.Messaging.Server.StoreLog (StoreLogRecord (..), closeStoreLog)
5152
import Simplex.Messaging.Transport
53+
import Simplex.Messaging.Transport.Credentials
5254
import Simplex.Messaging.Util (whenM)
5355
import Simplex.Messaging.Version (mkVersionRange)
5456
import System.Directory (doesDirectoryExist, doesFileExist, removeDirectoryRecursive, removeFile)
@@ -78,6 +80,8 @@ serverTests = do
7880
describe "GET & SUB commands" testGetSubCommands
7981
describe "Exceeding queue quota" testExceedQueueQuota
8082
describe "Concurrent sending and delivery" testConcurrentSendDelivery
83+
describe "Service message subscriptions" $ do
84+
testServiceDeliverSubscribe
8185
describe "Store log" testWithStoreLog
8286
describe "Restore messages" testRestoreMessages
8387
describe "Restore messages (old / v2)" testRestoreExpireMessages
@@ -104,6 +108,9 @@ pattern New rPub dhPub = NEW (NewQueueReq rPub dhPub Nothing SMSubscribe (Just (
104108
pattern Ids :: RecipientId -> SenderId -> RcvPublicDhKey -> BrokerMsg
105109
pattern Ids rId sId srvDh <- IDS (QIK rId sId srvDh _sndSecure _linkId Nothing Nothing)
106110

111+
pattern Ids_ :: RecipientId -> SenderId -> RcvPublicDhKey -> ServiceId -> BrokerMsg
112+
pattern Ids_ rId sId srvDh serviceId <- IDS (QIK rId sId srvDh _sndSecure _linkId (Just serviceId) Nothing)
113+
107114
pattern Msg :: MsgId -> MsgBody -> BrokerMsg
108115
pattern Msg msgId body <- MSG RcvMessage {msgId, msgBody = EncRcvMsgBody body}
109116

@@ -129,10 +136,15 @@ serviceSignSendRecv h pk serviceKey t = do
129136
pure r
130137

131138
signSendRecv_ :: forall c p. (Transport c, PartyI p) => THandleSMP c 'TClient -> C.APrivateAuthKey -> Maybe C.PrivateKeyEd25519 -> (ByteString, EntityId, Command p) -> IO (NonEmpty (Transmission (Either ErrorType BrokerMsg)))
132-
signSendRecv_ h@THandle {params} (C.APrivateAuthKey a pk) serviceKey_ (corrId, qId, cmd) = do
139+
signSendRecv_ h pk serviceKey_ t = do
140+
signSend_ h pk serviceKey_ t
141+
liftIO $ tGetClient h
142+
143+
signSend_ :: forall c p. (Transport c, PartyI p) => THandleSMP c 'TClient -> C.APrivateAuthKey -> Maybe C.PrivateKeyEd25519 -> (ByteString, EntityId, Command p) -> IO ()
144+
signSend_ h@THandle {params} (C.APrivateAuthKey a pk) serviceKey_ (corrId, qId, cmd) = do
133145
let TransmissionForAuth {tForAuth, tToSend} = encodeTransmissionForAuth params (CorrId corrId, qId, cmd)
134146
Right () <- tPut1 h (authorize tForAuth, tToSend)
135-
liftIO $ tGetClient h
147+
pure ()
136148
where
137149
authorize t = (,(`C.sign'` t) <$> serviceKey_) <$> case a of
138150
C.SEd25519 -> Just . TASignature . C.ASignature C.SEd25519 $ C.sign' pk t'
@@ -653,6 +665,68 @@ testConcurrentSendDelivery =
653665
Resp "4" _ OK <- signSendRecv rh rKey ("4", rId, ACK mId2)
654666
pure ()
655667

668+
testServiceDeliverSubscribe :: SpecWith (ASrvTransport, AStoreType)
669+
testServiceDeliverSubscribe =
670+
it "" $ \(at@(ATransport t), msType) -> do
671+
g <- C.newRandom
672+
creds <- genCredentials g Nothing (0, 2400) "localhost"
673+
let (_fp, tlsCred) = tlsCredentials [creds]
674+
serviceKeys@(_, servicePK) <- atomically $ C.generateKeyPair g
675+
let aServicePK = C.APrivateAuthKey C.SEd25519 servicePK
676+
withSmpServerConfigOn at (cfgMS msType) testPort $ \_ -> runClient t $ \h -> do
677+
(rPub, rKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g
678+
(dhPub, dhPriv :: C.PrivateKeyX25519) <- atomically $ C.generateKeyPair g
679+
(sPub, sKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g
680+
681+
(rId, sId, dec, serviceId) <- runServiceClient t (tlsCred, serviceKeys) $ \sh -> do
682+
Resp "1" NoEntity (ERR SERVICE) <- signSendRecv sh rKey ("1", NoEntity, New rPub dhPub)
683+
Resp "2" NoEntity (Ids_ rId sId srvDh serviceId) <- serviceSignSendRecv sh rKey servicePK ("2", NoEntity, New rPub dhPub)
684+
let dec = decryptMsgV3 $ C.dh' srvDh dhPriv
685+
Resp "3" sId' OK <- signSendRecv h sKey ("3", sId, SKEY sPub)
686+
sId' `shouldBe` sId
687+
Resp "4" _ OK <- signSendRecv h sKey ("4", sId, _SEND "hello")
688+
Resp "5" _ OK <- signSendRecv h sKey ("5", sId, _SEND "hello 2")
689+
Resp "" rId' (Msg mId1 msg1) <- tGet1 sh
690+
rId' `shouldBe` rId
691+
dec mId1 msg1 `shouldBe` Right "hello"
692+
-- ACK doesn't need service signature
693+
Resp "6" _ (Msg mId2 msg2) <- signSendRecv sh rKey ("6", rId, ACK mId1)
694+
dec mId2 msg2 `shouldBe` Right "hello 2"
695+
Resp "7" _ (ERR NO_MSG) <- signSendRecv sh rKey ("7", rId, ACK mId1)
696+
Resp "8" _ OK <- signSendRecv sh rKey ("8", rId, ACK mId2)
697+
Resp "9" _ OK <- signSendRecv h sKey ("9", sId, _SEND "hello 3")
698+
pure (rId, sId, dec, serviceId)
699+
700+
runServiceClient t (tlsCred, serviceKeys) $ \sh -> do
701+
Resp "10" NoEntity (ERR (CMD NO_AUTH)) <- signSendRecv sh aServicePK ("10", NoEntity, SUBS)
702+
mId3 <- signSendRecv sh aServicePK ("11", serviceId, SUBS) >>= \case -- possible race between SOKS response and MSG
703+
Resp "11" serviceId' (SOKS n) -> do
704+
n `shouldBe` 1
705+
serviceId' `shouldBe` serviceId
706+
Resp "" rId'' (Msg mId3 msg3) <- tGet1 sh
707+
rId'' `shouldBe` rId
708+
dec mId3 msg3 `shouldBe` Right "hello 3"
709+
pure mId3
710+
Resp "" rId'' (Msg mId3 msg3) -> do
711+
rId'' `shouldBe` rId
712+
dec mId3 msg3 `shouldBe` Right "hello 3"
713+
Resp "11" serviceId' (SOKS n) <- tGet1 sh
714+
n `shouldBe` 1
715+
serviceId' `shouldBe` serviceId
716+
pure mId3
717+
r -> error $ "unexpected response " <> take 100 (show r)
718+
Resp "12" _ OK <- signSendRecv sh rKey ("12", rId, ACK mId3)
719+
Resp "14" _ OK <- signSendRecv h sKey ("14", sId, _SEND "hello 4")
720+
Resp "" _ (Msg mId4 msg4) <- tGet1 sh
721+
dec mId4 msg4 `shouldBe` Right "hello 4"
722+
Resp "15" _ OK <- signSendRecv sh rKey ("15", rId, ACK mId4)
723+
pure ()
724+
where
725+
runClient :: Transport c => TProxy c 'TServer -> (THandleSMP c 'TClient -> IO a) -> IO a
726+
runClient _ test' = testSMPClient test'
727+
runServiceClient :: Transport c => TProxy c 'TServer -> (TLS.Credential, C.KeyPairEd25519) -> (THandleSMP c 'TClient -> IO a) -> IO a
728+
runServiceClient _ serviceCreds test' = testSMPServiceClient serviceCreds test'
729+
656730
testWithStoreLog :: SpecWith (ASrvTransport, AStoreType)
657731
testWithStoreLog =
658732
it "should store simplex queues to log and restore them after server restart" $ \(at@(ATransport t), msType) -> do

0 commit comments

Comments
 (0)