smp: strict 24-byte nonce for forwarded corrId

This commit is contained in:
sh
2026-10-04 13:07:40 +00:00
parent a84caa1073
commit 987915d633
6 changed files with 66 additions and 128 deletions
+2 -2
View File
@@ -1263,14 +1263,14 @@ proxySMPCommand c@ProtocolClient {thParams = proxyThParams, client_ = PClient {c
-- proxy should send PRES to the client with EncResponse
-- Always uses background timeout mode
forwardSMPTransmission :: SMPClient -> CorrId -> VersionSMP -> C.PublicKeyX25519 -> EncTransmission -> ExceptT SMPClientError IO (Maybe C.CbNonce, EncResponse)
forwardSMPTransmission c@ProtocolClient {thParams, client_ = PClient {clientCorrId = g}} fwdCorrId fwdVersion fwdKey fwdTransmission = do
forwardSMPTransmission c@ProtocolClient {thParams, client_ = PClient {clientCorrId = g}} (CorrId corrId) fwdVersion fwdKey fwdTransmission = do
-- prepare params
sessSecret <- case thAuth thParams of
Nothing -> throwE $ PCETransportError TENoServerAuth
Just THAuthClient {sessSecret} -> maybe (throwE $ PCETransportError TENoServerAuth) pure sessSecret
nonce <- liftIO . atomically $ C.randomCbNonce g
-- wrap
let fwdT = FwdTransmission {fwdCorrId, fwdVersion, fwdKey, fwdTransmission}
let fwdT = FwdTransmission {fwdCorrId = C.StrictCbNonce corrId, fwdVersion, fwdKey, fwdTransmission}
eft = EncFwdTransmission $ C.cbEncryptNoPad sessSecret nonce (smpEncode fwdT)
-- send
sendProtocolCommand_ c NRMBackground (Just nonce) Nothing Nothing NoEntity (Cmd SProxyService (RFWD eft)) >>= \case
+9
View File
@@ -142,6 +142,7 @@ module Simplex.Messaging.Crypto
-- * NaCl crypto_box
CbNonce (unCbNonce),
pattern CbNonce,
StrictCbNonce (..),
cbEncrypt,
cbEncryptNoPad,
cbEncryptMaxLenBS,
@@ -1426,6 +1427,14 @@ instance Encoding CbNonce where
smpEncode = unCbNonce
smpP = CryptoBoxNonce <$> A.take 24
-- | Length-prefixed nonce that fails to parse unless it is 24 bytes, unlike cbNonce that pads or truncates.
newtype StrictCbNonce = StrictCbNonce {unStrictCbNonce :: ByteString}
deriving (Eq, Show)
instance Encoding StrictCbNonce where
smpEncode (StrictCbNonce s) = smpEncode s
smpP = smpP >>= \s -> if B.length s == 24 then pure (StrictCbNonce s) else fail "bad nonce length"
newtype SbKey = SecretBoxKey {unSbKey :: ByteString}
deriving (Eq, Show)
deriving newtype (FromField)
+11 -22
View File
@@ -125,7 +125,6 @@ module Simplex.Messaging.Protocol
SrvLoc (..),
CorrId (..),
pattern NoCorrId,
corrIdNonce,
EntityId (..),
pattern NoEntity,
QueueId,
@@ -251,7 +250,6 @@ import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy as LB
import Data.Char (isPrint, isSpace)
import Data.Constraint (Dict (..))
import Data.Either (isLeft)
import Data.Functor (($>))
import Data.Int (Int64)
import Data.Kind
@@ -711,18 +709,18 @@ encTransmissionNonce v nonce@(C.CbNonce s)
| otherwise = nonce
data FwdTransmission = FwdTransmission
{ fwdCorrId :: CorrId,
{ fwdCorrId :: C.StrictCbNonce,
fwdVersion :: VersionSMP,
fwdKey :: C.PublicKeyX25519,
fwdTransmission :: EncTransmission
}
instance Encoding FwdTransmission where
smpEncode FwdTransmission {fwdCorrId = CorrId corrId, fwdVersion, fwdKey, fwdTransmission = EncTransmission t} =
smpEncode (corrId, fwdVersion, fwdKey, Tail t)
smpEncode FwdTransmission {fwdCorrId, fwdVersion, fwdKey, fwdTransmission = EncTransmission t} =
smpEncode (fwdCorrId, fwdVersion, fwdKey, Tail t)
smpP = do
(corrId, fwdVersion, fwdKey, Tail t) <- smpP
pure FwdTransmission {fwdCorrId = CorrId corrId, fwdVersion, fwdKey, fwdTransmission = EncTransmission t}
(fwdCorrId, fwdVersion, fwdKey, Tail t) <- smpP
pure FwdTransmission {fwdCorrId, fwdVersion, fwdKey, fwdTransmission = EncTransmission t}
newtype EncFwdTransmission = EncFwdTransmission ByteString
deriving (Show)
@@ -768,16 +766,16 @@ newtype EncFwdResponse = EncFwdResponse ByteString
deriving (Eq, Show)
data FwdResponse = FwdResponse
{ fwdCorrId :: CorrId,
{ fwdCorrId :: C.StrictCbNonce,
fwdResponse :: EncResponse
}
instance Encoding FwdResponse where
smpEncode FwdResponse {fwdCorrId = CorrId corrId, fwdResponse = EncResponse t} =
smpEncode (corrId, Tail t)
smpEncode FwdResponse {fwdCorrId, fwdResponse = EncResponse t} =
smpEncode (fwdCorrId, Tail t)
smpP = do
(corrId, Tail t) <- smpP
pure FwdResponse {fwdCorrId = CorrId corrId, fwdResponse = EncResponse t}
(fwdCorrId, Tail t) <- smpP
pure FwdResponse {fwdCorrId, fwdResponse = EncResponse t}
newtype EncResponse = EncResponse ByteString
deriving (Eq, Show)
@@ -1435,10 +1433,6 @@ instance IsString CorrId where
fromString = CorrId . fromString
{-# INLINE fromString #-}
-- | Fails unless corrId is exactly 24 bytes, so it is never padded or truncated.
corrIdNonce :: CorrId -> Either String C.CbNonce
corrIdNonce = smpDecode . bs
instance StrEncoding CorrId where
strEncode (CorrId cId) = strEncode cId
strDecode s = CorrId <$> strDecode s
@@ -2427,12 +2421,7 @@ tDecodeServer THandleParams {sessionId, thVersion = v, implySessId} = \case
| implySessId || sessId == sessionId -> case decodeTAuthBytes authenticator serviceSig of
Right tAuth -> bimap t ((tAuth,authorized,) . t) cmdOrErr
where
cmdOrErr
| invalidAuthNonce = Left $ fromProtocolError @v @err @cmd PECmdSyntax
| otherwise = parseProtocol @v @err @cmd v command >>= checkCredentials tAuth entityId
invalidAuthNonce = case tAuth of
Just (TAAuthenticator _, _) -> isLeft $ corrIdNonce corrId
_ -> False
cmdOrErr = parseProtocol @v @err @cmd v command >>= checkCredentials tAuth entityId
t :: a -> (CorrId, EntityId, a)
t = (corrId,entityId,)
Left _ -> tError corrId PEBlock
+17 -19
View File
@@ -64,7 +64,7 @@ import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy.Char8 as LB
import Data.Constraint (Dict (..))
import Data.Dynamic (toDyn)
import Data.Either (fromRight, isLeft, partitionEithers)
import Data.Either (fromRight, partitionEithers)
import Data.Foldable (foldrM)
import Data.Functor (($>), (<&>))
import Data.IORef
@@ -1453,22 +1453,20 @@ client
Just THAuthClient {peerServerCertKey} -> PKEY srvSessId vr peerServerCertKey
Nothing -> ERR $ transportErr TENoServerAuth
_ -> ERR $ transportErr TEVersion
PFWD fwdV pubKey encBlock
| isLeft (corrIdNonce corrId) -> pure $ Just $ ERR $ CMD SYNTAX
| otherwise -> do
ProxyAgent {smpAgent = a} <- asks proxyAgent
ServerStats {pMsgFwds, pMsgFwdsOwn} <- asks serverStats
let inc = mkIncProxyStats pMsgFwds pMsgFwdsOwn
liftIO (lookupSMPServerClient a sessId) >>= \case
Just (own, smp) -> do
inc own pRequests
forkProxiedCmd $ do
liftIO (runExceptT (forwardSMPTransmission smp corrId fwdV pubKey encBlock) `E.catches` clientHandlers) >>= \case
Right (nonce_, r) -> PRES nonce_ r <$ inc own pSuccesses
Left e -> ERR (smpProxyError e) <$ case e of
PCEProtocolError {} -> inc own pSuccesses
_ -> inc own pErrorsOther
Nothing -> inc False pRequests >> inc False pErrorsConnect $> Just (ERR $ PROXY NO_SESSION)
PFWD fwdV pubKey encBlock -> do
ProxyAgent {smpAgent = a} <- asks proxyAgent
ServerStats {pMsgFwds, pMsgFwdsOwn} <- asks serverStats
let inc = mkIncProxyStats pMsgFwds pMsgFwdsOwn
liftIO (lookupSMPServerClient a sessId) >>= \case
Just (own, smp) -> do
inc own pRequests
forkProxiedCmd $ do
liftIO (runExceptT (forwardSMPTransmission smp corrId fwdV pubKey encBlock) `E.catches` clientHandlers) >>= \case
Right (nonce_, r) -> PRES nonce_ r <$ inc own pSuccesses
Left e -> ERR (smpProxyError e) <$ case e of
PCEProtocolError {} -> inc own pSuccesses
_ -> inc own pErrorsOther
Nothing -> inc False pRequests >> inc False pErrorsConnect $> Just (ERR $ PROXY NO_SESSION)
where
forkProxiedCmd :: M s BrokerMsg -> M s (Maybe BrokerMsg)
forkProxiedCmd = forkCmd serverClientConcurrency corrId (EntityId sessId)
@@ -2127,12 +2125,12 @@ client
processForwardedCommand (EncFwdTransmission s) = fmap (either (Just . ERR) id) . runExceptT $ do
THAuthServer {serverPrivKey, sessSecret'} <- maybe (throwE $ transportErr TENoServerAuth) pure (thAuth thParams')
sessSecret <- maybe (throwE $ transportErr TENoServerAuth) pure sessSecret'
proxyNonce <- liftEitherWith (const $ CMD SYNTAX) $ corrIdNonce corrId
let proxyNonce = C.cbNonce $ bs corrId
s' <- liftEitherWith (const CRYPTO) $ C.cbDecryptNoPad sessSecret proxyNonce s
FwdTransmission {fwdCorrId, fwdVersion, fwdKey, fwdTransmission = EncTransmission et} <- liftEitherWith (const $ CMD SYNTAX) $ smpDecode s'
unless (fwdVersion `isCompatible` thServerVRange thParams') $ throwE $ transportErr TEVersion
clientNonce <- liftEitherWith (const $ CMD SYNTAX) $ corrIdNonce fwdCorrId
let clientSecret = C.dh' fwdKey serverPrivKey
clientNonce = C.cbNonce $ C.unStrictCbNonce fwdCorrId
b <- liftEitherWith (const CRYPTO) $ C.cbDecrypt clientSecret (encTransmissionNonce fwdVersion clientNonce) et
let clntTHParams = smpTHParamsSetVersion fwdVersion thParams'
-- only allowing single forwarded transactions
+20 -48
View File
@@ -21,12 +21,13 @@ import Control.Logger.Simple
import Control.Monad (forM, forM_, forever, replicateM_)
import Control.Monad.Trans.Except (ExceptT, runExceptT)
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as B
import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as L
import Data.Time.Clock (getCurrentTime)
import SMPAgentClient
import SMPClient
import ServerTests (decryptMsgV3, sendRecv, wrongSizeCorrIds)
import ServerTests (decryptMsgV3, sendRecv)
import Simplex.Messaging.Agent hiding (createConnection, joinConnection, sendMessage)
import qualified Simplex.Messaging.Agent as A
import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..))
@@ -36,13 +37,11 @@ import Simplex.Messaging.Client
import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.Crypto.Ratchet (pattern PQSupportOn)
import qualified Simplex.Messaging.Crypto.Ratchet as CR
import Simplex.Messaging.Encoding (smpEncode)
import Simplex.Messaging.Protocol (EncRcvMsgBody (..), MsgBody, QueueReqData (..), RcvMessage (..), SubscriptionMode (..), maxMessageLength, noMsgFlags)
import qualified Simplex.Messaging.Protocol as SMP
import Simplex.Messaging.Server.Env.STM (AStoreType (..), ServerConfig (..))
import Simplex.Messaging.Server.MsgStore.Types (SQSType (..))
import Simplex.Messaging.Transport
import qualified Simplex.Messaging.Transport.Client as TC
import Simplex.Messaging.Util (bshow, tshow)
import Simplex.Messaging.Version (mkVersionRange)
import System.FilePath (splitExtensions)
@@ -60,9 +59,6 @@ smpProxyTests = do
describe "server configuration" $ do
it "refuses proxy handshake unless enabled" testNoProxy
it "checks basic auth in proxy requests" testProxyAuth
describe "corrId used as nonce" $ do
it "proxy rejects PFWD with corrId of wrong size" testPFWDCorrIdSize
it "relay rejects RFWD with corrId of wrong size" testRFWDCorrIdSize
describe "relay reconnection" $ do
it "recovers when unresponsive relay restarts (control, no disconnect)" $ \_ ->
testProxyRecoversWithoutDisconnect
@@ -80,6 +76,8 @@ smpProxyTests = do
xit "batching proxy requests" todo
it "relay rejects forwarded command with changed version" $ \_ ->
testChangedFwdVersion
it "relay rejects forwarded corrId of wrong size" $ \_ ->
testFwdCorrIdSize
describe "deliver message via SMP proxy" $ do
let srv1 = SMPServer testHost testPort testKeyHash
srv2 = SMPServer testHost2 testPort2 testKeyHash
@@ -459,48 +457,6 @@ testProxyAuth msType = do
where
proxyCfgAuth = updateCfg (proxyCfgMS msType) $ \cfg_ -> cfg_ {newQueueBasicAuth = Just "correct"}
testPFWDCorrIdSize :: AStoreType -> IO ()
testPFWDCorrIdSize msType =
withSmpServerConfigOn (transport @TLS) (proxyCfgMS msType) testPort $ \_ ->
testSMPClient_ "127.0.0.1" testPort supportedServerSMPRelayVRange Nothing $ \(th@THandle {params} :: THandleSMP TLS 'TClient) -> do
g <- C.newRandom
(cmdKey, _ :: C.PrivateKeyX25519) <- atomically $ C.generateKeyPair g
let pfwd corrId = do
(_, _, reply) <- sendRecv th (Nothing, corrId, SMP.EntityId "unknown session", SMP.PFWD (thVersion params) cmdKey $ SMP.EncTransmission "invalid")
pure reply
corrId <- atomically $ C.randomBytes 24 g
pfwd corrId `shouldReturn` Right (SMP.ERR $ SMP.PROXY SMP.NO_SESSION)
forM_ (wrongSizeCorrIds corrId) $ \badCorrId ->
pfwd badCorrId `shouldReturn` Right (SMP.ERR $ SMP.CMD SMP.SYNTAX)
testRFWDCorrIdSize :: AStoreType -> IO ()
testRFWDCorrIdSize msType =
withSmpServerConfigOn (transport @TLS) (cfgMS msType) testPort2 $ \_ ->
testProxyModeClient $ \th@THandle {params = THandleParams {thVersion, thAuth}} -> do
Just THAuthClient {sessSecret = Just sessSecret} <- pure thAuth
g <- C.newRandom
(fwdKey, _ :: C.PrivateKeyX25519) <- atomically $ C.generateKeyPair g
let rfwd corrId fwdCorrId = do
let fwdT = SMP.FwdTransmission {fwdCorrId = SMP.CorrId fwdCorrId, fwdVersion = thVersion, fwdKey, fwdTransmission = SMP.EncTransmission "invalid"}
eft = SMP.EncFwdTransmission $ C.cbEncryptNoPad sessSecret (C.cbNonce corrId) (smpEncode fwdT)
(_, _, reply) <- sendRecv th (Nothing, corrId, NoEntity, SMP.RFWD eft)
pure reply
corrId <- atomically $ C.randomBytes 24 g
fwdCorrId <- atomically $ C.randomBytes 24 g
rfwd corrId fwdCorrId `shouldReturn` Right (SMP.ERR SMP.CRYPTO)
forM_ (wrongSizeCorrIds corrId) $ \badCorrId -> do
rfwd badCorrId fwdCorrId `shouldReturn` Right (SMP.ERR $ SMP.CMD SMP.SYNTAX)
rfwd corrId badCorrId `shouldReturn` Right (SMP.ERR $ SMP.CMD SMP.SYNTAX)
-- Connect to the relay on testPort2 the same way the proxy does, with the session secret.
testProxyModeClient :: (THandleSMP TLS 'TClient -> IO a) -> IO a
testProxyModeClient client = do
g <- C.newRandom
ks <- atomically $ C.generateKeyPair g
let tcConfig = TC.defaultTransportClientConfig {TC.clientALPN = Just alpnSupportedSMPHandshakes}
TC.runTransportClient tcConfig Nothing "127.0.0.1" testPort2 (Just testKeyHash) $ \h ->
runExceptT (smpClientHandshake h (Just ks) testKeyHash supportedClientSMPRelayVRange True Nothing) >>= either (fail . show) client
-- Connect a sender client to the proxy and request a relay session to testSMPServer2 (PRXY).
-- On success the reply is PKEY; otherwise it is the proxy error for the relay connection.
requestRelaySession :: IO (Either SMP.ErrorType SMP.BrokerMsg)
@@ -523,6 +479,22 @@ testChangedFwdVersion =
_ <- runExceptT' $ forward v
runExceptT (forward $ prevVersion v) `shouldReturn` Left (PCEProtocolError SMP.CRYPTO)
testFwdCorrIdSize :: IO ()
testFwdCorrIdSize =
withSmpServerConfigOn (transport @TLS) cfg testPort $ \_ -> do
g <- C.newRandom
ts <- getCurrentTime
rc <- either (fail . show) pure =<< getProtocolClient g NRMInteractive (1, testSMPServer, Nothing) defaultSMPClientConfig [] Nothing ts (\_ -> pure ())
THAuthClient {peerServerPubKey} <- maybe (fail "getProtocolClient returned no thAuth") pure $ thAuth $ thParams rc
(cmdPubKey, cmdPrivKey) <- atomically $ C.generateKeyPair g
nonce@(C.CbNonce corrId) <- atomically $ C.randomCbNonce g
let v = currentClientSMPRelayVersion
et <- either (fail . show) (pure . SMP.EncTransmission) $ C.cbEncrypt (C.dh' peerServerPubKey cmdPrivKey) (SMP.encTransmissionNonce v nonce) "" SMP.paddedProxiedTLength
let forward cId = runExceptT $ forwardSMPTransmission rc (SMP.CorrId cId) v cmdPubKey et
Right _ <- forward corrId
forM_ (["", B.take 23 corrId, corrId <> "x"] :: [ByteString]) $ \cId ->
forward cId `shouldReturn` Left (PCEProtocolError $ SMP.CMD SMP.SYNTAX)
-- Shared "phase 2" of the reconnection tests: start a healthy relay, confirm it is reachable
-- directly (PING, not via the proxy) so a proxy failure can only mean the proxy didn't reconnect,
-- let any stored connection error expire, then require the proxy to establish the session (PKEY).
+7 -37
View File
@@ -97,7 +97,6 @@ serverTests = do
describe "Restore messages (old / v2)" testRestoreExpireMessages
describe "Save prometheus metrics" testPrometheusMetrics
describe "Timing of AUTH error" testTiming
describe "Authenticator nonce" testAuthenticatorCorrIdSize
describe "Message notifications" $ do
testMessageNotifications
testMessageServiceNotifications
@@ -1303,9 +1302,9 @@ testTiming =
g <- C.newRandom
(rPub, rKey) <- atomically $ C.generateAuthKeyPair goodKeyAlg g
(dhPub, dhPriv :: C.PrivateKeyX25519) <- atomically $ C.generateKeyPair g
Resp "abcdabcdabcdabcdabcdabcd" NoEntity (Ids rId sId srvDh) <- signSendRecv rh rKey ("abcdabcdabcdabcdabcdabcd", NoEntity, New rPub dhPub)
Resp "abcd" NoEntity (Ids rId sId srvDh) <- signSendRecv rh rKey ("abcd", NoEntity, New rPub dhPub)
let dec = decryptMsgV3 $ C.dh' srvDh dhPriv
Resp "cdabcdabcdabcdabcdabcdab" _ resp <- signSendRecv rh rKey ("cdabcdabcdabcdabcdabcdab", rId, SUB)
Resp "cdab" _ resp <- signSendRecv rh rKey ("cdab", rId, SUB)
case resp of
OK -> pure ()
SOK Nothing -> pure ()
@@ -1315,9 +1314,9 @@ testTiming =
runTimingTest rh badKey rId SUB
(sPub, sKey) <- atomically $ C.generateAuthKeyPair goodKeyAlg g
Resp "dabcdabcdabcdabcdabcdabc" _ OK <- signSendRecv rh rKey ("dabcdabcdabcdabcdabcdabc", rId, KEY sPub)
Resp "dabc" _ OK <- signSendRecv rh rKey ("dabc", rId, KEY sPub)
Resp "bcdabcdabcdabcdabcdabcda" _ OK <- signSendRecv sh sKey ("bcdabcdabcdabcdabcdabcda", sId, _SEND "hello")
Resp "bcda" _ OK <- signSendRecv sh sKey ("bcda", sId, _SEND "hello")
Resp "" _ (Msg mId msg) <- tGet1 rh
(dec mId msg, Right "hello") #== "delivered from queue"
@@ -1328,15 +1327,15 @@ testTiming =
threadDelay 100000
_ <- timeRepeat n $ do
-- "warm up" the server
Resp "dabcdabcdabcdabcdabcdabc" _ (ERR AUTH) <- signSendRecv h badKey ("dabcdabcdabcdabcdabcdabc", EntityId "1234", cmd)
Resp "dabc" _ (ERR AUTH) <- signSendRecv h badKey ("dabc", EntityId "1234", cmd)
return ()
threadDelay 100000
timeWrongKey <- timeRepeat n $ do
Resp "cdabcdabcdabcdabcdabcdab" _ (ERR AUTH) <- signSendRecv h badKey ("cdabcdabcdabcdabcdabcdab", qId, cmd)
Resp "cdab" _ (ERR AUTH) <- signSendRecv h badKey ("cdab", qId, cmd)
return ()
threadDelay 100000
timeNoQueue <- timeRepeat n $ do
Resp "dabcdabcdabcdabcdabcdabc" _ (ERR AUTH) <- signSendRecv h badKey ("dabcdabcdabcdabcdabcdabc", EntityId "1234", cmd)
Resp "dabc" _ (ERR AUTH) <- signSendRecv h badKey ("dabc", EntityId "1234", cmd)
return ()
let ok = similarTime timeNoQueue timeWrongKey msType
unless ok . putStrLn . unwords $
@@ -1348,35 +1347,6 @@ testTiming =
]
ok `shouldBe` True
testAuthenticatorCorrIdSize :: SpecWith (ASrvTransport, AStoreType)
testAuthenticatorCorrIdSize =
it "should reject X25519 authenticator unless corrId is 24 bytes" $ \(ATransport t, msType) ->
smpTest2 t msType $ \r s -> do
g <- C.newRandom
let newCorrId = atomically $ C.randomBytes 24 g
rejected cId entId = (CorrId cId, entId, Right (ERR $ CMD SYNTAX))
badCorrIds <- wrongSizeCorrIds <$> newCorrId
(rPub, rKey) <- atomically $ C.generateAuthKeyPair C.SX25519 g
(dhPub, dhPriv :: C.PrivateKeyX25519) <- atomically $ C.generateKeyPair g
forM_ badCorrIds $ \cId ->
signSendRecv r rKey (cId, NoEntity, New rPub dhPub) `shouldReturn` rejected cId NoEntity
corrId1 <- newCorrId
Resp _ NoEntity (Ids rId sId srvDh) <- signSendRecv r rKey (corrId1, NoEntity, New rPub dhPub)
(sPub, sKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g
corrId2 <- newCorrId
Resp _ _ OK <- signSendRecv r rKey (corrId2, rId, KEY sPub)
Resp "bcda" _ OK <- signSendRecv s sKey ("bcda", sId, _SEND "hello")
Resp "" _ (Msg mId msg) <- tGet1 r
(decryptMsgV3 (C.dh' srvDh dhPriv) mId msg, Right "hello") #== "delivered from queue"
forM_ badCorrIds $ \cId ->
signSendRecv r rKey (cId, rId, ACK mId) `shouldReturn` rejected cId rId
corrId3 <- newCorrId
Resp _ _ OK <- signSendRecv r rKey (corrId3, rId, ACK mId)
pure ()
wrongSizeCorrIds :: ByteString -> [ByteString]
wrongSizeCorrIds corrId = ["", B.take 4 corrId, corrId <> "x"]
testMessageNotifications :: SpecWith (ASrvTransport, AStoreType)
testMessageNotifications =
it "should create simplex connection, subscribe notifier and deliver notifications" $ \(ATransport t, msType) -> do