diff --git a/contributing/CODE.md b/contributing/CODE.md index 85b4ce96c..c644a1606 100644 --- a/contributing/CODE.md +++ b/contributing/CODE.md @@ -41,6 +41,11 @@ Some files that use CPP language extension cannot be formatted as a whole, so in - Never do refactoring unless it substantially reduces cost of solving the current problem, including the cost of refactoring - Aim to minimize the code changes - do what is minimally required to solve users' problems +**Code analysis and review:** +- Trace data flows end-to-end: from origin, through storage/parameters, to consumption. Flag values that are discarded and reconstructed from partial data (e.g. extracted from a URI missing original fields) — this is usually a bug. +- Read implementations of called functions, not just signatures — if duplication involves a called function, check whether decomposing it resolves the duplication. +- Do not save time on analysis. Read every function in the data flow even when the interface seems clear — wrong assumptions about internals are the main source of missed bugs. + ### Haskell Extensions - `StrictData` enabled by default - Use STM for safe concurrency diff --git a/src/Simplex/Messaging/Agent.hs b/src/Simplex/Messaging/Agent.hs index 116c32b70..5623496ca 100644 --- a/src/Simplex/Messaging/Agent.hs +++ b/src/Simplex/Messaging/Agent.hs @@ -59,6 +59,8 @@ module Simplex.Messaging.Agent deleteConnectionAsync, deleteConnectionsAsync, createConnection, + prepareConnectionLink, + createConnectionForLink, setConnShortLink, deleteConnShortLink, getConnShortLink, @@ -398,6 +400,19 @@ createConnection :: ConnectionModeI c => AgentClient -> NetworkRequestMode -> Us createConnection c nm userId enableNtfs checkNotices = withAgentEnv c .::. newConn c nm userId enableNtfs checkNotices {-# INLINE createConnection #-} +-- | Prepare connection link for contact mode (no network call). +-- Returns root key pair (for signing OwnerAuth), the created link, and internal params. +-- The link address is fully determined at this point. +prepareConnectionLink :: AgentClient -> UserId -> Maybe ByteString -> Bool -> Maybe CRClientData -> AE (C.KeyPairEd25519, CreatedConnLink 'CMContact, PreparedLinkParams) +prepareConnectionLink c userId linkEntityId checkNotices = withAgentEnv c . prepareConnectionLink' c userId linkEntityId checkNotices +{-# INLINE prepareConnectionLink #-} + +-- | Create connection for prepared link (single network call). +-- Validates that server response matches the prepared link. +createConnectionForLink :: AgentClient -> NetworkRequestMode -> UserId -> Bool -> CreatedConnLink 'CMContact -> PreparedLinkParams -> UserConnLinkData 'CMContact -> CR.InitialKeys -> SubscriptionMode -> AE ConnId +createConnectionForLink c nm userId enableNtfs = withAgentEnv c .::. createConnectionForLink' c nm userId enableNtfs +{-# INLINE createConnectionForLink #-} + -- | Create or update user's contact connection short link setConnShortLink :: AgentClient -> NetworkRequestMode -> ConnId -> SConnectionMode c -> UserConnLinkData c -> Maybe CRClientData -> AE (ConnShortLink c) setConnShortLink c = withAgentEnv c .::. setConnShortLink' c @@ -902,6 +917,66 @@ newConn c nm userId enableNtfs checkNotices cMode linkData_ clientData pqInitKey <$> newRcvConnSrv c nm userId connId enableNtfs cMode linkData_ clientData pqInitKeys subMode srv `catchE` \e -> withStore' c (`deleteConnRecord` connId) >> throwE e +-- | Prepare connection link for contact mode (no network, no database). +-- Generates all cryptographic material and returns the link that will be created. +prepareConnectionLink' :: AgentClient -> UserId -> Maybe ByteString -> Bool -> Maybe CRClientData -> AM (C.KeyPairEd25519, CreatedConnLink 'CMContact, PreparedLinkParams) +prepareConnectionLink' c userId linkEntityId checkNotices clientData = do + g <- asks random + plpSrvWithAuth@(ProtoServerWithAuth srv _) <- getSMPServer c userId + when checkNotices $ checkClientNotices c plpSrvWithAuth + AgentConfig {smpClientVRange, smpAgentVRange} <- asks config + plpNonce@(C.CbNonce corrId) <- atomically $ C.randomCbNonce g + sigKeys@(_, plpRootPrivKey) <- atomically $ C.generateKeyPair g + plpQueueE2EKeys@(e2ePubKey, _) <- atomically $ C.generateKeyPair g + let sndId = SMP.EntityId $ B.take 24 $ C.sha3_384 corrId + qUri = SMPQueueUri smpClientVRange $ SMPQueueAddress srv sndId e2ePubKey (Just QMContact) + connReq = CRContactUri $ ConnReqUriData SSSimplex smpAgentVRange [qUri] clientData + (plpLinkKey, plpSignedFixedData) = SL.encodeSignFixedData sigKeys smpAgentVRange connReq linkEntityId + ccLink = CCLink connReq $ Just $ CSLContact SLSServer CCTContact srv plpLinkKey + params = PreparedLinkParams {plpNonce, plpQueueE2EKeys, plpLinkKey, plpRootPrivKey, plpSignedFixedData, plpSrvWithAuth} + pure (sigKeys, ccLink, params) + +-- | Create connection for prepared link (single network call). +createConnectionForLink' :: AgentClient -> NetworkRequestMode -> UserId -> Bool -> CreatedConnLink 'CMContact -> PreparedLinkParams -> UserConnLinkData 'CMContact -> CR.InitialKeys -> SubscriptionMode -> AM ConnId +createConnectionForLink' c nm userId enableNtfs (CCLink connReq _) PreparedLinkParams {plpNonce, plpQueueE2EKeys, plpLinkKey, plpRootPrivKey, plpSignedFixedData, plpSrvWithAuth} userLinkData pqInitKeys subMode = do + g <- asks random + AgentConfig {smpAgentVRange} <- asks config + case pqInitKeys of + CR.IKUsePQ -> throwE $ CMD PROHIBITED "createConnectionForLink" + _ -> pure () + connId <- newConnNoQueues c userId enableNtfs SCMContact (CR.connPQEncryption pqInitKeys) + let CRContactUri ConnReqUriData {crSmpQueues = SMPQueueUri _ SMPQueueAddress {senderId = sndId} :| _} = connReq + md = SL.encodeSignUserData SCMContact plpRootPrivKey smpAgentVRange userLinkData + linkData = (plpSignedFixedData, md) + qd <- encryptContactLinkData g plpRootPrivKey plpLinkKey sndId linkData + (_, qUri) <- + createRcvQueue c nm userId connId plpSrvWithAuth enableNtfs subMode (Just plpNonce) qd plpQueueE2EKeys + `catchE` \e -> withStore' c (`deleteConnRecord` connId) >> throwE e + let SMPQueueUri _ SMPQueueAddress {senderId = actualSndId} = qUri + unless (actualSndId == sndId) $ throwE $ INTERNAL "createConnectionForLink: sender ID mismatch" + pure connId + +-- | Encrypt signed link data for contact mode. +encryptContactLinkData :: TVar ChaChaDRG -> C.PrivateKeyEd25519 -> LinkKey -> SMP.SenderId -> (ByteString, ByteString) -> AM ClntQueueReqData +encryptContactLinkData g privSigKey linkKey sndId linkData = do + let (linkId, k) = SL.contactShortLinkKdf linkKey + srvData <- liftError id $ SL.encryptLinkData g k linkData + pure $ CQRContact $ Just CQRData {linkKey, privSigKey, srvReq = (linkId, (sndId, srvData))} + +-- | Shared helper: create receive queue and set up subscriptions. +createRcvQueue :: AgentClient -> NetworkRequestMode -> UserId -> ConnId -> SMPServerWithAuth -> Bool -> SubscriptionMode -> Maybe C.CbNonce -> ClntQueueReqData -> C.KeyPairX25519 -> AM (RcvQueue, SMPQueueUri) +createRcvQueue c nm userId connId srvWithAuth@(ProtoServerWithAuth srv _) enableNtfs subMode nonce_ qd e2eKeys = do + AgentConfig {smpClientVRange = vr} <- asks config + ntfServer_ <- if enableNtfs then newQueueNtfServer else pure Nothing + (rq, qUri, tSess, sessId) <- + newRcvQueue_ c nm userId connId srvWithAuth vr qd (isJust ntfServer_) subMode nonce_ e2eKeys + `catchAllErrors` \e -> liftIO (print e) >> throwE e + atomically $ incSMPServerStat c userId srv connCreated + rq' <- withStore c $ \db -> updateNewConnRcv db connId rq subMode + lift . when (subMode == SMSubscribe) $ addNewQueueSubscription c rq' tSess sessId + mapM_ (newQueueNtfSubscription c rq') ntfServer_ + pure (rq', qUri) + checkClientNotices :: AgentClient -> SMPServerWithAuth -> AM () checkClientNotices AgentClient {clientNotices, presetServers} (ProtoServerWithAuth srv@(ProtocolServer {host}) _) = do notices <- readTVarIO clientNotices @@ -978,7 +1053,7 @@ setConnShortLink' c nm connId cMode userLinkData clientData = sigKeys@(_, privSigKey) <- atomically $ C.generateKeyPair @'C.Ed25519 g let qUri = SMPQueueUri vr $ (rcvSMPQueueAddress rq) {queueMode = Just QMContact} connReq = CRContactUri $ ConnReqUriData SSSimplex smpAgentVRange [qUri] clientData - (linkKey, linkData) = SL.encodeSignLinkData sigKeys smpAgentVRange connReq ud + (linkKey, linkData) = SL.encodeSignLinkData sigKeys smpAgentVRange connReq Nothing ud (linkId, k) = SL.contactShortLinkKdf linkKey srvData <- liftError id $ SL.encryptLinkData g k linkData let slCreds = ShortLinkCreds linkId linkKey privSigKey Nothing (fst srvData) @@ -1065,25 +1140,15 @@ newRcvConnSrv c nm userId connId enableNtfs cMode userLinkData_ clientData pqIni case userLinkData_ of Just d -> do (nonce, qUri, cReq, qd) <- prepareLinkData d $ fst e2eKeys - (rq, qUri') <- createRcvQueue (Just nonce) qd e2eKeys + (rq, qUri') <- createRcvQueue c nm userId connId srvWithAuth enableNtfs subMode (Just nonce) qd e2eKeys ccLink <- connReqWithShortLink qUri cReq qUri' (shortLink rq) pure (ccLink, clientServiceId rq) Nothing -> do let qd = case cMode of SCMContact -> CQRContact Nothing; SCMInvitation -> CQRMessaging Nothing - (rq, qUri) <- createRcvQueue Nothing qd e2eKeys + (rq, qUri) <- createRcvQueue c nm userId connId srvWithAuth enableNtfs subMode Nothing qd e2eKeys cReq <- createConnReq qUri pure (CCLink cReq Nothing, clientServiceId rq) where - createRcvQueue :: Maybe C.CbNonce -> ClntQueueReqData -> C.KeyPairX25519 -> AM (RcvQueue, SMPQueueUri) - createRcvQueue nonce_ qd e2eKeys = do - AgentConfig {smpClientVRange = vr} <- asks config - ntfServer_ <- if enableNtfs then newQueueNtfServer else pure Nothing - (rq, qUri, tSess, sessId) <- newRcvQueue_ c nm userId connId srvWithAuth vr qd (isJust ntfServer_) subMode nonce_ e2eKeys `catchAllErrors` \e -> liftIO (print e) >> throwE e - atomically $ incSMPServerStat c userId srv connCreated - rq' <- withStore c $ \db -> updateNewConnRcv db connId rq subMode - lift . when (subMode == SMSubscribe) $ addNewQueueSubscription c rq' tSess sessId - mapM_ (newQueueNtfSubscription c rq') ntfServer_ - pure (rq', qUri) createConnReq :: SMPQueueUri -> AM (ConnectionRequestUri c) createConnReq qUri = do AgentConfig {smpAgentVRange, e2eEncryptVRange} <- asks config @@ -1107,12 +1172,9 @@ newRcvConnSrv c nm userId connId enableNtfs cMode userLinkData_ clientData pqIni qm = case cMode of SCMContact -> QMContact; SCMInvitation -> QMMessaging qUri = SMPQueueUri vr $ SMPQueueAddress srv sndId e2eDhKey (Just qm) connReq <- createConnReq qUri - let (linkKey, linkData) = SL.encodeSignLinkData sigKeys smpAgentVRange connReq userLinkData + let (linkKey, linkData) = SL.encodeSignLinkData sigKeys smpAgentVRange connReq Nothing userLinkData qd <- case cMode of - SCMContact -> do - let (linkId, k) = SL.contactShortLinkKdf linkKey - srvData <- liftError id $ SL.encryptLinkData g k linkData - pure $ CQRContact $ Just CQRData {linkKey, privSigKey, srvReq = (linkId, (sndId, srvData))} + SCMContact -> encryptContactLinkData g privSigKey linkKey sndId linkData SCMInvitation -> do let k = SL.invShortLinkKdf linkKey srvData <- liftError id $ SL.encryptLinkData g k linkData diff --git a/src/Simplex/Messaging/Agent/Protocol.hs b/src/Simplex/Messaging/Agent/Protocol.hs index d9aafe1ef..e988347ae 100644 --- a/src/Simplex/Messaging/Agent/Protocol.hs +++ b/src/Simplex/Messaging/Agent/Protocol.hs @@ -129,6 +129,7 @@ module Simplex.Messaging.Agent.Protocol ContactConnType (..), ShortLinkScheme (..), LinkKey (..), + PreparedLinkParams (..), StoredClientService (..), ClientService, ClientServiceId, @@ -1479,6 +1480,23 @@ newtype LinkKey = LinkKey ByteString -- sha3-256(fixed_data) instance ToField LinkKey where toField (LinkKey s) = toField $ Binary s +-- | Parameters for creating a connection with a prepared link. +data PreparedLinkParams = PreparedLinkParams + { -- | Correlation ID / determines sender ID + plpNonce :: C.CbNonce, + -- | Queue E2EE DH key pair + plpQueueE2EKeys :: C.KeyPairX25519, + -- | For encrypting link data + plpLinkKey :: LinkKey, + -- | Root signing key (for signing link data) + plpRootPrivKey :: C.PrivateKeyEd25519, + -- | smpEncode of FixedLinkData (includes linkEntityId) + plpSignedFixedData :: ByteString, + -- | Server with basic auth (not stored in link) + plpSrvWithAuth :: SMPServerWithAuth + } + deriving (Show) + instance ConnectionModeI c => ToField (ConnectionLink c) where toField = toField . Binary . strEncode instance (Typeable c, ConnectionModeI c) => FromField (ConnectionLink c) where fromField = blobFieldDecoder strDecode diff --git a/src/Simplex/Messaging/Crypto/ShortLink.hs b/src/Simplex/Messaging/Crypto/ShortLink.hs index ae9049889..013559fd1 100644 --- a/src/Simplex/Messaging/Crypto/ShortLink.hs +++ b/src/Simplex/Messaging/Crypto/ShortLink.hs @@ -13,7 +13,9 @@ module Simplex.Messaging.Crypto.ShortLink ( contactShortLinkKdf, invShortLinkKdf, encodeSignLinkData, + encodeSignFixedData, encodeSignUserData, + newOwnerAuth, encryptLinkData, encryptUserData, decryptLinkData, @@ -50,11 +52,16 @@ contactShortLinkKdf (LinkKey k) = invShortLinkKdf :: LinkKey -> C.SbKey invShortLinkKdf (LinkKey k) = C.unsafeSbKey $ C.hkdf "" k "SimpleXInvLink" 32 -encodeSignLinkData :: ConnectionModeI c => C.KeyPairEd25519 -> VersionRangeSMPA -> ConnectionRequestUri c -> UserConnLinkData c -> (LinkKey, (ByteString, ByteString)) -encodeSignLinkData (rootKey, pk) agentVRange linkConnReq userData = - let fd = smpEncode FixedLinkData {agentVRange, rootKey, linkConnReq, linkEntityId = Nothing} - md = smpEncode $ connLinkData agentVRange userData - in (LinkKey (C.sha3_256 fd), (encodeSign pk fd, encodeSign pk md)) +encodeSignLinkData :: forall c. ConnectionModeI c => C.KeyPairEd25519 -> VersionRangeSMPA -> ConnectionRequestUri c -> Maybe ByteString -> UserConnLinkData c -> (LinkKey, (ByteString, ByteString)) +encodeSignLinkData keys@(_, pk) agentVRange linkConnReq linkEntityId userData = + let (linkKey, fd) = encodeSignFixedData keys agentVRange linkConnReq linkEntityId + md = encodeSignUserData (sConnectionMode @c) pk agentVRange userData + in (linkKey, (fd, md)) + +encodeSignFixedData :: ConnectionModeI c => C.KeyPairEd25519 -> VersionRangeSMPA -> ConnectionRequestUri c -> Maybe ByteString -> (LinkKey, ByteString) +encodeSignFixedData (rootKey, pk) agentVRange linkConnReq linkEntityId = + let fd = smpEncode FixedLinkData {agentVRange, rootKey, linkConnReq, linkEntityId} + in (LinkKey (C.sha3_256 fd), encodeSign pk fd) encodeSignUserData :: ConnectionModeI c => SConnectionMode c -> C.PrivateKeyEd25519 -> VersionRangeSMPA -> UserConnLinkData c -> ByteString encodeSignUserData _ pk agentVRange userLinkData = @@ -68,6 +75,14 @@ connLinkData vr = \case encodeSign :: C.PrivateKeyEd25519 -> ByteString -> ByteString encodeSign pk s = smpEncode (C.sign' pk s) <> s +-- | Generate a new owner key pair and create OwnerAuth signed by the authorizing key. +-- ownerId is application-specific (e.g., MemberId in chat). +newOwnerAuth :: TVar ChaChaDRG -> OwnerId -> C.PrivateKeyEd25519 -> IO (C.PrivateKeyEd25519, OwnerAuth) +newOwnerAuth g ownerId signingKey = do + (ownerKey, ownerPrivKey) <- atomically $ C.generateKeyPair @'C.Ed25519 g + let authOwnerSig = C.sign' signingKey $ ownerId <> C.encodePubKey ownerKey + pure (ownerPrivKey, OwnerAuth {ownerId, ownerKey, authOwnerSig}) + encryptLinkData :: TVar ChaChaDRG -> C.SbKey -> (ByteString, ByteString) -> ExceptT AgentErrorType IO QueueLinkData encryptLinkData g k = bimapM (encrypt fixedDataPaddedLength) (encrypt userDataPaddedLength) where diff --git a/tests/AgentTests/FunctionalAPITests.hs b/tests/AgentTests/FunctionalAPITests.hs index 77cd9b6bd..b87ebcfad 100644 --- a/tests/AgentTests/FunctionalAPITests.hs +++ b/tests/AgentTests/FunctionalAPITests.hs @@ -383,6 +383,7 @@ functionalAPITests ps = do it "should connect via contact short link after restart" $ testContactShortLinkRestart ps it "should connect via added contact short link after restart" $ testAddContactShortLinkRestart ps it "should create and get short links with the old contact queues" $ testOldContactQueueShortLink ps + it "should connect via prepared connection link" $ testPrepareCreateConnectionLink ps describe "Message delivery" $ do describe "update connection agent version on received messages" $ do it "should increase if compatible, shouldn'ps decrease" $ @@ -1646,6 +1647,36 @@ replaceSubstringInFile filePath oldText newText = do let newContent = T.replace oldText newText content T.writeFile filePath newContent +testPrepareCreateConnectionLink :: HasCallStack => (ASrvTransport, AStoreType) -> IO () +testPrepareCreateConnectionLink ps = withSmpServer ps $ withAgentClients2 $ \a b -> do + let userData = UserLinkData "test user data" + userCtData = UserContactData {direct = True, owners = [], relays = [], userData} + userLinkData = UserContactLinkData userCtData + g <- C.newRandom + linkEntId <- atomically $ C.randomBytes 32 g + runRight $ do + ((_rootPubKey, _rootPrivKey), ccLink@(CCLink connReq (Just shortLink)), preparedParams) <- + A.prepareConnectionLink a 1 (Just linkEntId) True Nothing + liftIO $ strDecode (strEncode shortLink) `shouldBe` Right shortLink + _ <- A.createConnectionForLink a NRMInteractive 1 True ccLink preparedParams userLinkData CR.IKPQOn SMSubscribe + (FixedLinkData {linkConnReq = connReq', linkEntityId}, ContactLinkData _ userCtData') <- getConnShortLink b 1 shortLink + liftIO $ Just linkEntId `shouldBe` linkEntityId + Right connReqDecoded <- pure $ smpDecode (smpEncode connReq) + liftIO $ connReq' `shouldBe` connReqDecoded + liftIO $ userCtData' `shouldBe` userCtData + (bId, sndSecure) <- joinConnection b 1 True connReq' "bob's connInfo" SMSubscribe + liftIO $ sndSecure `shouldBe` False + ("", _, REQ invId _ "bob's connInfo") <- get a + aId <- A.prepareConnectionToAccept a 1 True invId PQSupportOn + (sndSecure', Nothing) <- acceptContact a 1 aId True invId "alice's connInfo" PQSupportOn SMSubscribe + liftIO $ sndSecure' `shouldBe` True + ("", _, CONF confId _ "alice's connInfo") <- get b + allowConnection b bId confId "bob's connInfo" + get a ##> ("", aId, INFO "bob's connInfo") + get a ##> ("", aId, CON) + get b ##> ("", bId, CON) + exchangeGreetings a aId b bId + testIncreaseConnAgentVersion :: HasCallStack => (ASrvTransport, AStoreType) -> IO () testIncreaseConnAgentVersion ps = do alice <- getSMPAgentClient' 1 agentCfg {smpAgentVRange = mkVersionRange 1 2} initAgentServers testDB diff --git a/tests/AgentTests/ShortLinkTests.hs b/tests/AgentTests/ShortLinkTests.hs index 97472bec1..edffaa3d9 100644 --- a/tests/AgentTests/ShortLinkTests.hs +++ b/tests/AgentTests/ShortLinkTests.hs @@ -44,7 +44,7 @@ testInvShortLink = do sigKeys <- atomically $ C.generateKeyPair @'C.Ed25519 g let userData = UserLinkData "some user data" userLinkData = UserInvLinkData userData - (linkKey, linkData) = SL.encodeSignLinkData sigKeys supportedSMPAgentVRange invConnRequest userLinkData + (linkKey, linkData) = SL.encodeSignLinkData sigKeys supportedSMPAgentVRange invConnRequest Nothing userLinkData k = SL.invShortLinkKdf linkKey Right srvData <- runExceptT $ SL.encryptLinkData g k linkData -- decrypt @@ -59,7 +59,7 @@ testInvShortLinkBadDataHash = do sigKeys <- atomically $ C.generateKeyPair @'C.Ed25519 g let userData = UserLinkData "some user data" userLinkData = UserInvLinkData userData - (_linkKey, linkData) = SL.encodeSignLinkData sigKeys supportedSMPAgentVRange invConnRequest userLinkData + (_linkKey, linkData) = SL.encodeSignLinkData sigKeys supportedSMPAgentVRange invConnRequest Nothing userLinkData -- different key linkKey <- LinkKey <$> atomically (C.randomBytes 32 g) let k = SL.invShortLinkKdf linkKey @@ -82,7 +82,7 @@ testContactShortLink = do let userData = UserLinkData "some user data" userCtData = UserContactData {direct = True, owners = [], relays = [], userData} userLinkData = UserContactLinkData userCtData - (linkKey, linkData) = SL.encodeSignLinkData sigKeys supportedSMPAgentVRange contactConnRequest userLinkData + (linkKey, linkData) = SL.encodeSignLinkData sigKeys supportedSMPAgentVRange contactConnRequest Nothing userLinkData (_linkId, k) = SL.contactShortLinkKdf linkKey Right srvData <- runExceptT $ SL.encryptLinkData g k linkData -- decrypt @@ -98,7 +98,7 @@ testUpdateContactShortLink = do let userData = UserLinkData "some user data" userCtData = UserContactData {direct = True, owners = [], relays = [], userData} userLinkData = UserContactLinkData userCtData - (linkKey, linkData) = SL.encodeSignLinkData sigKeys supportedSMPAgentVRange contactConnRequest userLinkData + (linkKey, linkData) = SL.encodeSignLinkData sigKeys supportedSMPAgentVRange contactConnRequest Nothing userLinkData (_linkId, k) = SL.contactShortLinkKdf linkKey Right (fd, _ud) <- runExceptT $ SL.encryptLinkData g k linkData -- encrypt updated user data @@ -119,7 +119,7 @@ testContactShortLinkBadDataHash = do sigKeys <- atomically $ C.generateKeyPair @'C.Ed25519 g let userData = UserLinkData "some user data" userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData} - (_linkKey, linkData) = SL.encodeSignLinkData sigKeys supportedSMPAgentVRange contactConnRequest userLinkData + (_linkKey, linkData) = SL.encodeSignLinkData sigKeys supportedSMPAgentVRange contactConnRequest Nothing userLinkData -- different key linkKey <- LinkKey <$> atomically (C.randomBytes 32 g) let (_linkId, k) = SL.contactShortLinkKdf linkKey @@ -135,7 +135,7 @@ testContactShortLinkBadSignature = do sigKeys <- atomically $ C.generateKeyPair @'C.Ed25519 g let userData = UserLinkData "some user data" userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData} - (linkKey, linkData) = SL.encodeSignLinkData sigKeys supportedSMPAgentVRange contactConnRequest userLinkData + (linkKey, linkData) = SL.encodeSignLinkData sigKeys supportedSMPAgentVRange contactConnRequest Nothing userLinkData (_linkId, k) = SL.contactShortLinkKdf linkKey Right (fd, _ud) <- runExceptT $ SL.encryptLinkData g k linkData -- encrypt updated user data @@ -167,7 +167,7 @@ encryptLink g = do sigKeys@(_, pk) <- atomically $ C.generateKeyPair @'C.Ed25519 g let userData = UserLinkData "some user data" userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData} - (linkKey, linkData) = SL.encodeSignLinkData sigKeys supportedSMPAgentVRange contactConnRequest userLinkData + (linkKey, linkData) = SL.encodeSignLinkData sigKeys supportedSMPAgentVRange contactConnRequest Nothing userLinkData (_linkId, k) = SL.contactShortLinkKdf linkKey Right (fd, _ud) <- runExceptT $ SL.encryptLinkData g k linkData pure (pk, (fd, linkKey, k))