Skip to content

Commit 42dbb88

Browse files
authored
ntf server: allow retries when creating subscriptions, prohibit subscriptions with the same queue but another notifier key or token (#1525)
* ntf server: allow retries when creating subscriptions, prohibit subscriptions with the same queue but another notifier key or token * sync files in the test * refactor
1 parent 850d2fa commit 42dbb88

File tree

5 files changed

+134
-97
lines changed

5 files changed

+134
-97
lines changed

src/Simplex/Messaging/Notifications/Server.hs

Lines changed: 47 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,8 @@ runNtfClientTransport th@THandle {params} = do
537537
s <- asks subscriber
538538
ps <- asks pushServer
539539
expCfg <- asks $ inactiveClientExpiration . config
540-
raceAny_ ([liftIO $ send th c, client c s ps, receive th c] <> disconnectThread_ c expCfg)
540+
st <- asks store
541+
raceAny_ ([liftIO $ send th c, client c s ps, liftIO $ receive st th c] <> disconnectThread_ c expCfg)
541542
`finally` liftIO (clientDisconnected c)
542543
where
543544
disconnectThread_ c (Just expCfg) = [liftIO $ disconnectTransport th (rcvActiveAt c) (sndActiveAt c) expCfg (pure True)]
@@ -546,10 +547,10 @@ runNtfClientTransport th@THandle {params} = do
546547
clientDisconnected :: NtfServerClient -> IO ()
547548
clientDisconnected NtfServerClient {connected} = atomically $ writeTVar connected False
548549

549-
receive :: Transport c => THandleNTF c 'TServer -> NtfServerClient -> M ()
550-
receive th@THandle {params = THandleParams {thAuth}} NtfServerClient {rcvQ, sndQ, rcvActiveAt} = forever $ do
551-
ts <- L.toList <$> liftIO (tGet th)
552-
atomically . (writeTVar rcvActiveAt $!) =<< liftIO getSystemTime
550+
receive :: Transport c => NtfPostgresStore -> THandleNTF c 'TServer -> NtfServerClient -> IO ()
551+
receive st th@THandle {params = THandleParams {thAuth}} NtfServerClient {rcvQ, sndQ, rcvActiveAt} = forever $ do
552+
ts <- L.toList <$> tGet th
553+
atomically . (writeTVar rcvActiveAt $!) =<< getSystemTime
553554
(errs, cmds) <- partitionEithers <$> mapM cmdAction ts
554555
write sndQ errs
555556
write rcvQ cmds
@@ -560,70 +561,60 @@ receive th@THandle {params = THandleParams {thAuth}} NtfServerClient {rcvQ, sndQ
560561
logError $ "invalid client request: " <> tshow e
561562
pure $ Left (corrId, entId, NRErr e)
562563
Right cmd ->
563-
verified =<< verifyNtfTransmission ((,C.cbNonce (SMP.bs corrId)) <$> thAuth) t cmd
564+
verified =<< verifyNtfTransmission st ((,C.cbNonce (SMP.bs corrId)) <$> thAuth) t cmd
564565
where
565566
verified = \case
566567
VRVerified req -> pure $ Right req
567-
VRFailed -> do
568+
VRFailed e -> do
568569
logError "unauthorized client request"
569-
pure $ Left (corrId, entId, NRErr AUTH)
570+
pure $ Left (corrId, entId, NRErr e)
570571
write q = mapM_ (atomically . writeTBQueue q) . L.nonEmpty
571572

572573
send :: Transport c => THandleNTF c 'TServer -> NtfServerClient -> IO ()
573574
send h@THandle {params} NtfServerClient {sndQ, sndActiveAt} = forever $ do
574575
ts <- atomically $ readTBQueue sndQ
575-
void . liftIO $ tPut h $ L.map (\t -> Right (Nothing, encodeTransmission params t)) ts
576-
atomically . (writeTVar sndActiveAt $!) =<< liftIO getSystemTime
577-
578-
data VerificationResult = VRVerified NtfRequest | VRFailed
579-
580-
verifyNtfTransmission :: Maybe (THandleAuth 'TServer, C.CbNonce) -> SignedTransmission ErrorType NtfCmd -> NtfCmd -> M VerificationResult
581-
verifyNtfTransmission auth_ (tAuth, authorized, (corrId, entId, _)) cmd = do
582-
st <- asks store
583-
case cmd of
584-
-- TODO [ntfdb] this looks suspicious, as if it can prevent repeated registrations
585-
NtfCmd SToken c@(TNEW tkn@(NewNtfTkn _ k _)) -> do
586-
r_ <- liftIO $ getNtfTokenRegistration st tkn
587-
pure $
588-
if verifyCmdAuthorization auth_ tAuth authorized k
589-
then case r_ of
590-
Right t@NtfTknRec {tknVerifyKey}
591-
-- keys will be the same because of condition in `getNtfTokenRegistration`
592-
| k == tknVerifyKey -> VRVerified $ tknCmd t c
593-
| otherwise -> VRFailed
594-
Left _ -> VRVerified (NtfReqNew corrId (ANE SToken tkn))
595-
else VRFailed
596-
NtfCmd SToken c -> do
597-
t_ <- liftIO $ getNtfToken st entId
598-
verifyToken_' t_ (`tknCmd` c)
599-
NtfCmd SSubscription c@(SNEW sub@(NewNtfSub tknId smpQueue _)) ->
600-
liftIO $ verify <$> findNtfSubscription st tknId smpQueue
601-
where
602-
verify = \case
603-
Right (t, s_) -> verifyToken t $ case s_ of
604-
Nothing -> NtfReqNew corrId (ANE SSubscription sub)
605-
Just s -> subCmd s c
606-
-- TODO [ntfdb] it should simply return error if it is not AUTH
607-
Left _ -> maybe False (dummyVerifyCmd auth_ authorized) tAuth `seq` VRFailed
608-
NtfCmd SSubscription PING -> pure $ VRVerified $ NtfReqPing corrId entId
609-
NtfCmd SSubscription c -> liftIO $ verify <$> getNtfSubscription st entId
610-
where
611-
verify = \case
612-
Right (t, s) -> verifyToken t $ subCmd s c
613-
-- TODO [ntfdb] it should simply return error if it is not AUTH
614-
Left _ -> maybe False (dummyVerifyCmd auth_ authorized) tAuth `seq` VRFailed
576+
void $ tPut h $ L.map (\t -> Right (Nothing, encodeTransmission params t)) ts
577+
atomically . (writeTVar sndActiveAt $!) =<< getSystemTime
578+
579+
data VerificationResult = VRVerified NtfRequest | VRFailed ErrorType
580+
581+
verifyNtfTransmission :: NtfPostgresStore -> Maybe (THandleAuth 'TServer, C.CbNonce) -> SignedTransmission ErrorType NtfCmd -> NtfCmd -> IO VerificationResult
582+
verifyNtfTransmission st auth_ (tAuth, authorized, (corrId, entId, _)) = \case
583+
NtfCmd SToken c@(TNEW tkn@(NewNtfTkn _ k _))
584+
| verifyCmdAuthorization auth_ tAuth authorized k ->
585+
result <$> findNtfTokenRegistration st tkn
586+
| otherwise -> pure $ VRFailed AUTH
587+
where
588+
result = \case
589+
Right (Just t@NtfTknRec {tknVerifyKey})
590+
-- keys will be the same because of condition in `findNtfTokenRegistration`
591+
| k == tknVerifyKey -> VRVerified $ tknCmd t c
592+
| otherwise -> VRFailed AUTH
593+
Right Nothing -> VRVerified (NtfReqNew corrId (ANE SToken tkn))
594+
Left e -> VRFailed e
595+
NtfCmd SToken c -> either err verify <$> getNtfToken st entId
596+
where
597+
verify t = verifyToken t $ tknCmd t c
598+
NtfCmd SSubscription c@(SNEW sub@(NewNtfSub tknId smpQueue _)) ->
599+
either err verify <$> findNtfSubscription st tknId smpQueue
600+
where
601+
verify (t, s_) = verifyToken t $ case s_ of
602+
Nothing -> NtfReqNew corrId (ANE SSubscription sub)
603+
Just s -> subCmd s c
604+
NtfCmd SSubscription PING -> pure $ VRVerified $ NtfReqPing corrId entId
605+
NtfCmd SSubscription c -> either err verify <$> getNtfSubscription st entId
606+
where
607+
verify (t, s) = verifyToken t $ subCmd s c
615608
where
616609
tknCmd t c = NtfReqCmd SToken (NtfTkn t) (corrId, entId, c)
617610
subCmd s c = NtfReqCmd SSubscription (NtfSub s) (corrId, entId, c)
618-
verifyToken_' :: Either ErrorType NtfTknRec -> (NtfTknRec -> NtfRequest) -> M VerificationResult
619-
verifyToken_' t_ result = pure $ case t_ of
620-
Right t -> verifyToken t $ result t
621-
-- TODO [ntfdb] it should simply return error if it is not AUTH
622-
Left _ -> maybe False (dummyVerifyCmd auth_ authorized) tAuth `seq` VRFailed
623611
verifyToken :: NtfTknRec -> NtfRequest -> VerificationResult
624612
verifyToken NtfTknRec {tknVerifyKey} r
625613
| verifyCmdAuthorization auth_ tAuth authorized tknVerifyKey = VRVerified r
626-
| otherwise = VRFailed
614+
| otherwise = VRFailed AUTH
615+
err = \case -- signature verification for AUTH errors mitigates timing attacks for existence checks
616+
AUTH -> maybe False (dummyVerifyCmd auth_ authorized) tAuth `seq` VRFailed AUTH
617+
e -> VRFailed e
627618

628619
client :: NtfServerClient -> NtfSubscriber -> NtfPushServer -> M ()
629620
client NtfServerClient {rcvQ, sndQ} NtfSubscriber {newSubQ, smpAgent = ca} NtfPushServer {pushQ, intervalNotifiers} =
@@ -669,9 +660,6 @@ client NtfServerClient {rcvQ, sndQ} NtfSubscriber {newSubQ, smpAgent = ca} NtfPu
669660
pure NROk
670661
| otherwise -> do
671662
logDebug "TVFY - incorrect code or token status"
672-
liftIO $ print tkn
673-
let NtfRegCode c = code
674-
liftIO $ print $ B64.encode c
675663
pure $ NRErr AUTH
676664
TCHK -> do
677665
logDebug "TCHK"
@@ -732,17 +720,15 @@ client NtfServerClient {rcvQ, sndQ} NtfSubscriber {newSubQ, smpAgent = ca} NtfPu
732720
atomically $ writeTBQueue newSubQ (srv, [sub])
733721
incNtfStat subCreated
734722
pure $ NRSubId subId
735-
-- TODO [ntfdb] we must allow repeated inserts that don't change credentials
736723
False -> pure $ NRErr AUTH
737724
pure (corrId, NoEntity, resp)
738-
NtfReqCmd SSubscription (NtfSub NtfSubRec {smpQueue = SMPQueueNtf {smpServer, notifierId}, notifierKey = registeredNKey, subStatus}) (corrId, subId, cmd) -> do
725+
NtfReqCmd SSubscription (NtfSub NtfSubRec {ntfSubId, smpQueue = SMPQueueNtf {smpServer, notifierId}, notifierKey = registeredNKey, subStatus}) (corrId, subId, cmd) -> do
739726
(corrId,subId,) <$> case cmd of
740727
SNEW (NewNtfSub _ _ notifierKey) -> do
741728
logDebug "SNEW - existing subscription"
742-
-- possible improvement: retry if subscription failed, if pending or AUTH do nothing
743729
pure $
744730
if notifierKey == registeredNKey
745-
then NRSubId subId
731+
then NRSubId ntfSubId
746732
else NRErr AUTH
747733
SCHK -> do
748734
logDebug "SCHK"

src/Simplex/Messaging/Notifications/Server/Store/Postgres.hs

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -145,18 +145,20 @@ ntfTknToRow NtfTknRec {ntfTknId, token, tknStatus, tknVerifyKey, tknDhPrivKey, t
145145
in (ntfTknId, pp, Binary ppToken, tknStatus, tknVerifyKey, tknDhPrivKey, tknDhSecret, Binary regCode, tknCronInterval, tknUpdatedAt)
146146

147147
getNtfToken :: NtfPostgresStore -> NtfTokenId -> IO (Either ErrorType NtfTknRec)
148-
getNtfToken st tknId = getNtfToken_ st " WHERE token_id = ?" (Only tknId)
148+
getNtfToken st tknId =
149+
(maybe (Left AUTH) Right =<<) <$>
150+
getNtfToken_ st " WHERE token_id = ?" (Only tknId)
149151

150-
getNtfTokenRegistration :: NtfPostgresStore -> NewNtfEntity 'Token -> IO (Either ErrorType NtfTknRec)
151-
getNtfTokenRegistration st (NewNtfTkn (DeviceToken pp ppToken) tknVerifyKey _) =
152+
findNtfTokenRegistration :: NtfPostgresStore -> NewNtfEntity 'Token -> IO (Either ErrorType (Maybe NtfTknRec))
153+
findNtfTokenRegistration st (NewNtfTkn (DeviceToken pp ppToken) tknVerifyKey _) =
152154
getNtfToken_ st " WHERE push_provider = ? AND push_provider_token = ? AND verify_key = ?" (pp, Binary ppToken, tknVerifyKey)
153155

154-
getNtfToken_ :: ToRow q => NtfPostgresStore -> Query -> q -> IO (Either ErrorType NtfTknRec)
156+
getNtfToken_ :: ToRow q => NtfPostgresStore -> Query -> q -> IO (Either ErrorType (Maybe NtfTknRec))
155157
getNtfToken_ st cond params =
156-
withDB "getNtfToken" st $ \db -> runExceptT $ do
157-
tkn <- ExceptT $ firstRow rowToNtfTkn AUTH $ DB.query db (ntfTknQuery <> cond) params
158-
liftIO $ updateTokenDate st db tkn
159-
pure tkn
158+
withDB' "getNtfToken" st $ \db -> do
159+
tkn_ <- maybeFirstRow rowToNtfTkn $ DB.query db (ntfTknQuery <> cond) params
160+
mapM_ (updateTokenDate st db) tkn_
161+
pure tkn_
160162

161163
updateTokenDate :: NtfPostgresStore -> DB.Connection -> NtfTknRec -> IO ()
162164
updateTokenDate st db NtfTknRec {ntfTknId, tknUpdatedAt} = do
@@ -269,26 +271,28 @@ foldNtfSubscriptions st srv fetchCount state action =
269271
toNtfSub (ntfSubId, tokenId, nId, subStatus, notifierKey) =
270272
NtfSubRec {ntfSubId, tokenId, smpQueue = SMPQueueNtf srv nId, subStatus, notifierKey}
271273

274+
-- Returns token and subscription.
275+
-- If subscription exists but belongs to another token, returns Left AUTH
272276
findNtfSubscription :: NtfPostgresStore -> NtfTokenId -> SMPQueueNtf -> IO (Either ErrorType (NtfTknRec, Maybe NtfSubRec))
273-
findNtfSubscription st tknId q@(SMPQueueNtf srv nId) =
277+
findNtfSubscription st tknId q =
274278
withDB "findNtfSubscription" st $ \db -> runExceptT $ do
275-
r@(tkn@NtfTknRec {tknStatus}, _) <-
276-
ExceptT $ firstRow (rowToNtfTknMaybeSub q) AUTH $
279+
tkn@NtfTknRec {ntfTknId, tknStatus} <- ExceptT $ getNtfToken st tknId
280+
unless (allowNtfSubCommands tknStatus) $ throwE AUTH
281+
liftIO $ updateTokenDate st db tkn
282+
sub_ <-
283+
liftIO $ maybeFirstRow (rowToNtfSub q) $
277284
DB.query
278285
db
279286
[sql|
280-
SELECT t.token_id, t.push_provider, t.push_provider_token, t.status, t.verify_key, t.dh_priv_key, t.dh_secret, t.reg_code, t.cron_interval, t.updated_at,
281-
s.subscription_id, s.smp_notifier_key, s.status
282-
FROM tokens t
283-
LEFT JOIN subscriptions s ON s.token_id = t.token_id AND s.smp_notifier_id = ?
284-
LEFT JOIN smp_servers p ON p.smp_server_id = s.smp_server_id
285-
AND p.smp_host = ? AND p.smp_port = ? AND p.smp_keyhash = ?
286-
WHERE t.token_id = ?
287-
|]
288-
(Only nId :. srvToRow srv :. Only tknId)
289-
liftIO $ updateTokenDate st db tkn
290-
unless (allowNtfSubCommands tknStatus) $ throwE AUTH
291-
pure r
287+
SELECT s.token_id, s.subscription_id, s.smp_notifier_key, s.status
288+
FROM subscriptions s
289+
JOIN smp_servers p ON p.smp_server_id = s.smp_server_id
290+
WHERE p.smp_host = ? AND p.smp_port = ? AND p.smp_keyhash = ?
291+
AND s.smp_notifier_id = ?
292+
|]
293+
(smpQueueToRow q)
294+
forM_ sub_ $ \NtfSubRec {tokenId} -> unless (ntfTknId == tokenId) $ throwE AUTH
295+
pure (tkn, sub_)
292296

293297
getNtfSubscription :: NtfPostgresStore -> NtfSubscriptionId -> IO (Either ErrorType (NtfTknRec, NtfSubRec))
294298
getNtfSubscription st subId =
@@ -313,22 +317,15 @@ getNtfSubscription st subId =
313317

314318
type NtfSubRow = (NtfSubscriptionId, NtfPrivateAuthKey, NtfSubStatus)
315319

316-
type MaybeNtfSubRow = (Maybe NtfSubscriptionId, Maybe NtfPrivateAuthKey, Maybe NtfSubStatus)
317-
318320
rowToNtfTknSub :: NtfTknRow :. NtfSubRow :. SMPQueueNtfRow -> (NtfTknRec, NtfSubRec)
319321
rowToNtfTknSub (tknRow :. (ntfSubId, notifierKey, subStatus) :. qRow) =
320322
let tkn@NtfTknRec {ntfTknId = tokenId} = rowToNtfTkn tknRow
321323
smpQueue = rowToSMPQueue qRow
322324
in (tkn, NtfSubRec {ntfSubId, tokenId, smpQueue, notifierKey, subStatus})
323325

324-
rowToNtfTknMaybeSub :: SMPQueueNtf -> NtfTknRow :. MaybeNtfSubRow -> (NtfTknRec, Maybe NtfSubRec)
325-
rowToNtfTknMaybeSub smpQueue (tknRow :. subRow) =
326-
let tkn@NtfTknRec {ntfTknId = tokenId} = rowToNtfTkn tknRow
327-
sub_ = case subRow of
328-
(Just ntfSubId, Just notifierKey, Just subStatus) ->
329-
Just NtfSubRec {ntfSubId, tokenId, smpQueue, notifierKey, subStatus}
330-
_ -> Nothing
331-
in (tkn, sub_)
326+
rowToNtfSub :: SMPQueueNtf -> Only NtfTokenId :. NtfSubRow -> NtfSubRec
327+
rowToNtfSub smpQueue (Only tokenId :. (ntfSubId, notifierKey, subStatus)) =
328+
NtfSubRec {ntfSubId, tokenId, smpQueue, notifierKey, subStatus}
332329

333330
mkNtfSubRec :: NtfSubscriptionId -> NewNtfEntity 'Subscription -> NtfSubRec
334331
mkNtfSubRec ntfSubId (NewNtfSub tokenId smpQueue notifierKey) =

tests/AgentTests/NotificationTests.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ import Simplex.Messaging.Protocol (ErrorType (AUTH), MsgFlags (MsgFlags), NtfSer
7979
import qualified Simplex.Messaging.Protocol as SMP
8080
import Simplex.Messaging.Server.Env.STM (AStoreType (..), ServerConfig (..))
8181
import Simplex.Messaging.Transport (ATransport)
82+
import System.Process (callCommand)
8283
import Test.Hspec
8384
import UnliftIO
8485
#if defined(dbPostgres)
@@ -569,15 +570,15 @@ testNotificationSubscriptionExistingConnection apns baseId alice@AgentClient {ag
569570
threadDelay 500000
570571
suspendAgent alice 0
571572
closeDBStore store
572-
threadDelay 1500000
573+
callCommand "sync"
573574
putStrLn "before opening the database from another agent"
574575

575576
-- aliceNtf client doesn't have subscription and is allowed to get notification message
576577
withAgent 3 aliceCfg initAgentServers testDB $ \aliceNtf -> do
577578
(Just SMPMsgMeta {msgFlags = MsgFlags True}) :| _ <- getConnectionMessages aliceNtf [cId]
578579
pure ()
579580

580-
threadDelay 1500000
581+
callCommand "sync"
581582
putStrLn "after closing the database in another agent"
582583
reopenDBStore store
583584
foregroundAgent alice

tests/NtfServerTests.hs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
module NtfServerTests where
1414

1515
import Control.Concurrent (threadDelay)
16-
import Control.Monad (void)
1716
import qualified Data.Aeson as J
1817
import qualified Data.Aeson.Types as JT
1918
import Data.Bifunctor (first)
@@ -52,6 +51,7 @@ ntfServerTests t = do
5251
describe "Notifications server protocol syntax" $ ntfSyntaxTests t
5352
describe "Notification subscriptions (NKEY)" $ testNotificationSubscription t createNtfQueueNKEY
5453
-- describe "Notification subscriptions (NEW with ntf creds)" $ testNotificationSubscription t createNtfQueueNEW
54+
describe "Retried notification subscription" $ testRetriedNtfSubscription t
5555

5656
ntfSyntaxTests :: ATransport -> Spec
5757
ntfSyntaxTests (ATransport t) = do
@@ -179,6 +179,38 @@ testNotificationSubscription (ATransport t) createQueue =
179179
smpServer3 `shouldBe` srv
180180
notifierId3 `shouldBe` nId
181181

182+
testRetriedNtfSubscription :: ATransport -> Spec
183+
testRetriedNtfSubscription (ATransport t) =
184+
it "should allow retrying to create notification subscription with the same token and key" $ do
185+
g <- C.newRandom
186+
(sPub, _sKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g
187+
(nPub, nKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g
188+
withAPNSMockServer $ \apns ->
189+
smpTest' t $ \h ->
190+
ntfTest t $ \nh -> do
191+
((_sId, _rId, _rKey, _rcvDhSecret), nId, _rcvNtfDhSecret) <- createNtfQueueNKEY h sPub nPub
192+
(tknKey, _dhSecret, tId, regCode) <- registerToken nh apns "abcd"
193+
let srv = SMPServer SMP.testHost SMP.testPort SMP.testKeyHash
194+
q = SMPQueueNtf srv nId
195+
-- fails creating subscription until token is verified
196+
RespNtf "2" NoEntity (NRErr AUTH) <- signSendRecvNtf nh tknKey ("2", NoEntity, SNEW $ NewNtfSub tId q nKey)
197+
-- verify token
198+
RespNtf "3" tId1 NROk <- signSendRecvNtf nh tknKey ("3", tId, TVFY regCode)
199+
tId1 `shouldBe` tId
200+
-- create subscription
201+
RespNtf "4" NoEntity (NRSubId subId) <- signSendRecvNtf nh tknKey ("4", NoEntity, SNEW $ NewNtfSub tId q nKey)
202+
-- allow retry
203+
RespNtf "4a" NoEntity (NRSubId subId') <- signSendRecvNtf nh tknKey ("4a", NoEntity, SNEW $ NewNtfSub tId q nKey)
204+
subId' `shouldBe` subId
205+
-- fail with another key
206+
(_nPub, nKey') <- atomically $ C.generateAuthKeyPair C.SEd25519 g
207+
RespNtf "5" NoEntity (NRErr AUTH) <- signSendRecvNtf nh tknKey ("5", NoEntity, SNEW $ NewNtfSub tId q nKey')
208+
-- fail with another token
209+
(tknKey', _dhSecret, tId', regCode') <- registerToken nh apns "efgh"
210+
RespNtf "6" _ NROk <- signSendRecvNtf nh tknKey' ("6", tId', TVFY regCode')
211+
RespNtf "7" NoEntity (NRErr AUTH) <- signSendRecvNtf nh tknKey' ("7", NoEntity, SNEW $ NewNtfSub tId' q nKey)
212+
pure ()
213+
182214
type CreateQueueFunc =
183215
forall c.
184216
Transport c =>
@@ -197,6 +229,24 @@ createNtfQueueNKEY h sPub nPub = do
197229
let rcvNtfDhSecret = C.dh' rcvNtfSrvPubDhKey rcvNtfPrivDhKey
198230
pure ((sId, rId, rKey, rcvDhSecret), nId, rcvNtfDhSecret)
199231

232+
registerToken :: Transport c => THandleNTF c 'TClient -> APNSMockServer -> ByteString -> IO (C.APrivateAuthKey, C.DhSecretX25519, NtfEntityId, NtfRegCode)
233+
registerToken nh apns token = do
234+
g <- C.newRandom
235+
(tknPub, tknKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g
236+
(dhPub, dhPriv :: C.PrivateKeyX25519) <- atomically $ C.generateKeyPair g
237+
let tkn = DeviceToken PPApnsTest token
238+
RespNtf "1" NoEntity (NRTknId tId ntfDh) <- signSendRecvNtf nh tknKey ("1", NoEntity, TNEW $ NewNtfTkn tkn tknPub dhPub)
239+
APNSMockRequest {notification = APNSNotification {aps = APNSBackground _, notificationData = Just ntfData}} <-
240+
getMockNotification apns tkn
241+
let dhSecret = C.dh' ntfDh dhPriv
242+
decryptCode nd =
243+
let Right verification = nd .-> "verification"
244+
Right nonce = C.cbNonce <$> nd .-> "nonce"
245+
Right pt = C.cbDecrypt dhSecret nonce verification
246+
in NtfRegCode pt
247+
let code = decryptCode ntfData
248+
pure (tknKey, dhSecret, tId, code)
249+
200250
-- TODO [notifications]
201251
-- createNtfQueueNEW :: CreateQueueFunc
202252
-- createNtfQueueNEW h sPub nPub = do

tests/SMPClient.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,9 @@ smpServerTest _ t = runSmpTest (ASType SQSMemory SMSJournal) $ \h -> tPut' h t >
360360
smpTest :: (HasCallStack, Transport c) => TProxy c -> AStoreType -> (HasCallStack => THandleSMP c 'TClient -> IO ()) -> Expectation
361361
smpTest _ msType test' = runSmpTest msType test' `shouldReturn` ()
362362

363+
smpTest' :: forall c. (HasCallStack, Transport c) => TProxy c -> (HasCallStack => THandleSMP c 'TClient -> IO ()) -> Expectation
364+
smpTest' = (`smpTest` ASType SQSMemory SMSJournal)
365+
363366
smpTestN :: (HasCallStack, Transport c) => AStoreType -> Int -> (HasCallStack => [THandleSMP c 'TClient] -> IO ()) -> Expectation
364367
smpTestN msType n test' = runSmpTestN msType n test' `shouldReturn` ()
365368

0 commit comments

Comments
 (0)