From 30f87938a6f4947ad1336bcd26676c7cc2991360 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 23 Jul 2026 20:45:36 +0100 Subject: [PATCH] add pqRatchet param to address-related commands --- bots/api/COMMANDS.md | 7 ++-- bots/src/API/Docs/Commands.hs | 2 +- .../types/typescript/src/commands.ts | 3 +- .../src/simplex_chat/types/_commands.py | 3 +- src/Simplex/Chat/Bot.hs | 2 +- src/Simplex/Chat/Controller.hs | 6 +-- src/Simplex/Chat/Library/Commands.hs | 39 +++++++++++-------- 7 files changed, 35 insertions(+), 27 deletions(-) diff --git a/bots/api/COMMANDS.md b/bots/api/COMMANDS.md index 19569482ce..dcd22e07aa 100644 --- a/bots/api/COMMANDS.md +++ b/bots/api/COMMANDS.md @@ -239,20 +239,21 @@ Set bot address settings. **Parameters**: - userId: int64 +- pqRatchet: bool? - settings: [AddressSettings](./TYPES.md#addresssettings) **Syntax**: ``` -/_address_settings +/_address_settings [ pq_ratchet=on|off] ``` ```javascript -'/_address_settings ' + userId + ' ' + JSON.stringify(settings) // JavaScript +'/_address_settings ' + userId + (typeof pqRatchet == 'boolean' ? ' pq_ratchet=' + (pqRatchet ? 'on' : 'off') : '') + ' ' + JSON.stringify(settings) // JavaScript ``` ```python -'/_address_settings ' + str(userId) + ' ' + json.dumps(settings) # Python +'/_address_settings ' + str(userId) + ((' pq_ratchet=' + ('on' if pqRatchet else 'off')) if pqRatchet is not None else '') + ' ' + json.dumps(settings) # Python ``` **Responses**: diff --git a/bots/src/API/Docs/Commands.hs b/bots/src/API/Docs/Commands.hs index d4286f8e8a..e4a028dab6 100644 --- a/bots/src/API/Docs/Commands.hs +++ b/bots/src/API/Docs/Commands.hs @@ -81,7 +81,7 @@ chatCommandsDocsData = ("APIDeleteMyAddress", [], "Delete bot address.", ["CRUserContactLinkDeleted", "CRChatCmdError"], [], Just UNBackground, "/_delete_address " <> Param "userId"), ("APIShowMyAddress", [], "Get bot address and settings.", ["CRUserContactLink", "CRChatCmdError"], [], Nothing, "/_show_address " <> Param "userId"), ("APISetProfileAddress", [], "Add address to bot profile.", ["CRUserProfileUpdated", "CRChatCmdError"], [], Just UNInteractive, "/_profile_address " <> Param "userId" <> " " <> OnOff "enable"), - ("APISetAddressSettings", [], "Set bot address settings.", ["CRUserContactLinkUpdated", "CRChatCmdError"], [], Just UNInteractive, "/_address_settings " <> Param "userId" <> " " <> Json "settings") + ("APISetAddressSettings", [], "Set bot address settings.", ["CRUserContactLinkUpdated", "CRChatCmdError"], [], Just UNInteractive, "/_address_settings " <> Param "userId" <> OnOffParam "pq_ratchet" "pqRatchet" Nothing <> " " <> Json "settings") ] ), ( "Message commands", diff --git a/packages/simplex-chat-client/types/typescript/src/commands.ts b/packages/simplex-chat-client/types/typescript/src/commands.ts index 50965cd2fa..244b4c3447 100644 --- a/packages/simplex-chat-client/types/typescript/src/commands.ts +++ b/packages/simplex-chat-client/types/typescript/src/commands.ts @@ -70,6 +70,7 @@ export namespace APISetProfileAddress { // Network usage: interactive. export interface APISetAddressSettings { userId: number // int64 + pqRatchet?: boolean settings: T.AddressSettings } @@ -77,7 +78,7 @@ export namespace APISetAddressSettings { export type Response = CR.UserContactLinkUpdated | CR.ChatCmdError export function cmdString(self: APISetAddressSettings): string { - return '/_address_settings ' + self.userId + ' ' + JSON.stringify(self.settings) + return '/_address_settings ' + self.userId + (typeof self.pqRatchet == 'boolean' ? ' pq_ratchet=' + (self.pqRatchet ? 'on' : 'off') : '') + ' ' + JSON.stringify(self.settings) } } diff --git a/packages/simplex-chat-python/src/simplex_chat/types/_commands.py b/packages/simplex-chat-python/src/simplex_chat/types/_commands.py index 713cce872b..4e13af20c1 100644 --- a/packages/simplex-chat-python/src/simplex_chat/types/_commands.py +++ b/packages/simplex-chat-python/src/simplex_chat/types/_commands.py @@ -63,11 +63,12 @@ APISetProfileAddress_Response = CR.UserProfileUpdated | CR.ChatCmdError # Network usage: interactive. class APISetAddressSettings(TypedDict): userId: int # int64 + pqRatchet: NotRequired[bool] settings: "T.AddressSettings" def APISetAddressSettings_cmd_string(self: APISetAddressSettings) -> str: - return '/_address_settings ' + str(self['userId']) + ' ' + json.dumps(self['settings']) + return '/_address_settings ' + str(self['userId']) + ((' pq_ratchet=' + ('on' if self.get('pqRatchet') else 'off')) if self.get('pqRatchet') is not None else '') + ' ' + json.dumps(self['settings']) APISetAddressSettings_Response = CR.UserContactLinkUpdated | CR.ChatCmdError diff --git a/src/Simplex/Chat/Bot.hs b/src/Simplex/Chat/Bot.hs index 40ffa4486c..dfb7418f3f 100644 --- a/src/Simplex/Chat/Bot.hs +++ b/src/Simplex/Chat/Bot.hs @@ -66,7 +66,7 @@ initializeBotAddress' logAddress cc = do putStrLn $ "Bot's contact address is: " <> B.unpack (maybe (strEncode uri) strEncode shortUri) when (isJust shortUri) $ putStrLn $ "Full contact address for old clients: " <> B.unpack (strEncode uri) let settings = AddressSettings {businessAddress = False, autoAccept = Just AutoAccept {acceptIncognito = False}, autoReply = Nothing} - void $ sendChatCmd cc $ SetAddressSettings settings + void $ sendChatCmd cc $ SetAddressSettings Nothing settings sendMessage :: ChatController -> Contact -> Text -> IO () sendMessage cc ct = sendComposedMessage cc ct Nothing . MCText diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 4b14ca6b5c..d0eae16076 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -554,12 +554,12 @@ data ChatCommand | DeleteMyAddress | APIShowMyAddress {userId :: UserId} | ShowMyAddress - | APIAddMyAddressShortLink UserId + | APIAddMyAddressShortLink {userId :: UserId, pqRatchet :: Maybe Bool} | APIRotateAddressRatchetKeys UserId | APISetProfileAddress {userId :: UserId, enable :: Bool} | SetProfileAddress Bool - | APISetAddressSettings {userId :: UserId, settings :: AddressSettings} - | SetAddressSettings AddressSettings + | APISetAddressSettings {userId :: UserId, pqRatchet :: Maybe Bool, settings :: AddressSettings} + | SetAddressSettings {pqRatchet :: Maybe Bool, settings :: AddressSettings} | AcceptContact IncognitoEnabled ContactName | RejectContact ContactName | ForwardMessage {toChatName :: ChatName, fromContactName :: ContactName, forwardedMsg :: Text} diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index 4f1d99cb5d..6c24f37111 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -107,7 +107,7 @@ import qualified Simplex.Messaging.Crypto as C import qualified Simplex.Messaging.Crypto.ShortLink as SL import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..)) import qualified Simplex.Messaging.Crypto.File as CF -import Simplex.Messaging.Crypto.Ratchet (InitialKeys (..), PQEncryption (..), PQSupport (..), pattern IKPQOff, pattern IKPQOn, pattern PQSupportOff, pattern PQSupportOn) +import Simplex.Messaging.Crypto.Ratchet (E2ERatchetParamsUri (..), InitialKeys (..), PQEncryption (..), PQSupport (..), pattern IKPQOff, pattern IKPQOn, pattern PQSupportOff, pattern PQSupportOn) import Simplex.Messaging.Encoding import Simplex.Messaging.Encoding.String import Simplex.Messaging.Parsers (base64P) @@ -2444,10 +2444,12 @@ processChatCommand cxt nm = \case CRUserContactLink user <$> withFastStore (`getUserAddress` user) ShowMyAddress -> withUser' $ \User {userId} -> processChatCommand cxt nm $ APIShowMyAddress userId - APIAddMyAddressShortLink userId -> withUserId' userId $ \user -> - CRUserContactLink user <$> (withFastStore (`getUserAddress` user) >>= setMyAddressData False user) + APIAddMyAddressShortLink userId pqRatchet_ -> withUserId' userId $ \user -> do + -- TODO [address DR] remove the option, and use IKUsePQ + let pqInitKeys = (\case True -> IKUsePQ; False -> IKPQOn) <$> pqRatchet_ + CRUserContactLink user <$> (withFastStore (`getUserAddress` user) >>= setMyAddressData False pqInitKeys user) APIRotateAddressRatchetKeys userId -> withUserId' userId $ \user -> - CRUserContactLink user <$> (withFastStore (`getUserAddress` user) >>= setMyAddressData True user) + CRUserContactLink user <$> (withFastStore (`getUserAddress` user) >>= setMyAddressData True (Just IKUsePQ) user) APISetProfileAddress userId False -> withUserId userId $ \user@User {profile = p} -> do let p' = (fromLocalProfile p :: Profile) {contactLink = Nothing} updateProfile_ user p' True $ withFastStore' $ \db -> setUserProfileContactLink db user Nothing @@ -2458,7 +2460,7 @@ processChatCommand cxt nm = \case updateProfile_ user p' True $ withFastStore' $ \db -> setUserProfileContactLink db user $ Just ucl SetProfileAddress onOff -> withUser $ \User {userId} -> processChatCommand cxt nm $ APISetProfileAddress userId onOff - APISetAddressSettings userId settings@AddressSettings {businessAddress, autoAccept} -> withUserId userId $ \user -> do + APISetAddressSettings userId pqRatchet_ settings@AddressSettings {businessAddress, autoAccept} -> withUserId userId $ \user -> do ucl@UserContactLink {userContactLinkId, shortLinkDataSet, addressSettings} <- withFastStore (`getUserAddress` user) forM_ autoAccept $ \AutoAccept {acceptIncognito} -> do when (shortLinkDataSet && acceptIncognito) $ throwCmdError "incognito not allowed for address with short link data" @@ -2467,11 +2469,12 @@ processChatCommand cxt nm = \case then pure $ CRUserContactLinkUpdated user ucl else do let ucl' = ucl {addressSettings = settings} - ucl'' <- if shortLinkDataSet then setMyAddressData False user ucl' else pure ucl' + pqInitKeys = (\case True -> IKUsePQ; False -> IKPQOn) <$> pqRatchet_ + ucl'' <- if shortLinkDataSet then setMyAddressData False pqInitKeys user ucl' else pure ucl' withFastStore' $ \db -> updateUserAddressSettings db userContactLinkId settings pure $ CRUserContactLinkUpdated user ucl'' - SetAddressSettings settings -> withUser $ \User {userId} -> - processChatCommand cxt nm $ APISetAddressSettings userId settings + SetAddressSettings pqRatchet_ settings -> withUser $ \User {userId} -> + processChatCommand cxt nm $ APISetAddressSettings userId pqRatchet_ settings AcceptContact incognito cName -> withUser $ \User {userId} -> do connReqId <- withFastStore $ \db -> getContactRequestIdByName db userId cName processChatCommand cxt nm $ APIAcceptContact incognito connReqId @@ -3948,8 +3951,10 @@ processChatCommand cxt nm = \case setMyAddressData' :: User -> CM () setMyAddressData' user' = withFastStore' (\db -> runExceptT $ getUserAddress db user) >>= \case - Right ucl@UserContactLink {shortLinkDataSet} - | shortLinkDataSet -> void $ setMyAddressData False user' ucl + Right ucl@UserContactLink {shortLinkDataSet, connLinkContact = CCLink {connFullLink = CRContactUri _ e2e}} + | shortLinkDataSet -> + let pqInitKeys = (\(_, E2ERatchetParamsUri _ _ _ pq) -> if isJust pq then IKUsePQ else IKPQOn) <$> e2e + in void $ setMyAddressData False pqInitKeys user' ucl _ -> pure () sendUpdateToContacts :: User -> [Contact] -> CM UserProfileUpdateSummary sendUpdateToContacts user' contacts = do @@ -3990,8 +3995,8 @@ processChatCommand cxt nm = \case ctMsgReq ChangedProfileContact {conn} = fmap $ \SndMessage {msgId, msgBody} -> (conn, MsgFlags {notification = hasNotification XInfo_}, (vrValue msgBody, [msgId])) - setMyAddressData :: Bool -> User -> UserContactLink -> CM UserContactLink - setMyAddressData rotateKeys user@User {userChatRelay} ucl@UserContactLink {userContactLinkId, connLinkContact = CCLink connFullLink _, addressSettings} = do + setMyAddressData :: Bool -> Maybe InitialKeys -> User -> UserContactLink -> CM UserContactLink + setMyAddressData rotateKeys pqInitKeys user@User {userChatRelay} ucl@UserContactLink {userContactLinkId, connLinkContact = CCLink connFullLink _, addressSettings} = do conn <- withFastStore $ \db -> getUserAddressConnection db cxt user shortLinkProfile <- presentUserBadge user Nothing (userProfileDirect user Nothing Nothing True) -- TODO [short links] do not save address to server if data did not change, spinners, error handling @@ -3999,8 +4004,8 @@ processChatCommand cxt nm = \case | isTrue userChatRelay = relayShortLinkData shortLinkProfile | otherwise = contactShortLinkData shortLinkProfile $ Just addressSettings userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData, ratchetKeys = Nothing} - -- TODO [address DR] switch to (Just IKUsePQ) after rotateKeys - sLnk <- shortenShortLink' =<< withAgent (\a -> setConnShortLink a nm (aConnId conn) SCMContact userLinkData Nothing rotateKeys Nothing) + -- TODO [address DR] remove parameter and switch to (Just IKUsePQ) after rotateKeys + sLnk <- shortenShortLink' =<< withAgent (\a -> setConnShortLink a nm (aConnId conn) SCMContact userLinkData Nothing rotateKeys pqInitKeys) withFastStore' $ \db -> setUserContactLinkShortLink db userContactLinkId sLnk let autoAccept' = (\aa -> aa {acceptIncognito = False}) <$> autoAccept addressSettings ucl' = (ucl :: UserContactLink) {connLinkContact = CCLink connFullLink (Just sLnk), shortLinkDataSet = True, shortLinkLargeDataSet = BoolDef True, addressSettings = addressSettings {autoAccept = autoAccept'}} @@ -5708,12 +5713,12 @@ chatCommandP = ("/delete_address" <|> "/da") $> DeleteMyAddress, "/_show_address " *> (APIShowMyAddress <$> A.decimal), ("/show_address" <|> "/sa") $> ShowMyAddress, - "/_short_link_address " *> (APIAddMyAddressShortLink <$> A.decimal), + "/_short_link_address " *> (APIAddMyAddressShortLink <$> A.decimal <*> optional (" pq_ratchet=" *> onOffP)), "/_rotate_address_keys " *> (APIRotateAddressRatchetKeys <$> A.decimal), "/_profile_address " *> (APISetProfileAddress <$> A.decimal <* A.space <*> onOffP), ("/profile_address " <|> "/pa ") *> (SetProfileAddress <$> onOffP), - "/_address_settings " *> (APISetAddressSettings <$> A.decimal <* A.space <*> jsonP), - "/auto_accept " *> (SetAddressSettings <$> autoAcceptP), + "/_address_settings " *> (APISetAddressSettings <$> A.decimal <* A.space <*> optional (" pq_ratchet=" *> onOffP) <*> jsonP), + "/auto_accept " *> (SetAddressSettings <$> optional (" pq_ratchet=" *> onOffP) <*> autoAcceptP), ("/accept" <|> "/ac") *> (AcceptContact <$> incognitoP <* A.space <* char_ '@' <*> displayNameP), ("/reject " <|> "/rc ") *> char_ '@' *> (RejectContact <$> displayNameP), ("/markdown" <|> "/m") $> ChatHelp HSMarkdown,