smp client: api to send/receive data blobs directly and via proxy, tests (#1246)

* smp client: api to send/receive data blobs directly and via proxy, tests

* fix test
This commit is contained in:
Evgeny Poberezkin
2024-07-27 07:50:25 +01:00
committed by GitHub
parent fd009fe0d9
commit 4599dafa16
4 changed files with 118 additions and 17 deletions
+37 -12
View File
@@ -118,6 +118,8 @@ import Control.Monad.Trans.Except
import Crypto.Random (ChaChaDRG)
import qualified Data.Aeson.TH as J
import qualified Data.Attoparsec.ByteString.Char8 as A
import Data.Bitraversable (bimapM)
import qualified Data.ByteArray as BA
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as B
import Data.Functor (($>))
@@ -841,16 +843,34 @@ deleteSMPDataBlob :: SMPClient -> DataPrivateAuthKey -> BlobId -> ExceptT SMPCli
deleteSMPDataBlob = okSMPCommand CLR
{-# INLINE deleteSMPDataBlob #-}
getSMPDataBlob :: SMPClient -> BlobId -> ExceptT SMPClientError IO EncDataBlob
getSMPDataBlob c dId =
sendSMPCommand c Nothing dId READ >>= \case
DATA encBlob -> pure encBlob
-- pk is the private key passed to the client out of band.
-- Associated public key is used as ID to retrieve data blob
getSMPDataBlob :: SMPClient -> C.PrivateKeyX25519 -> ExceptT SMPClientError IO DataBlob
getSMPDataBlob c@ProtocolClient {thParams, client_ = PClient {clientCorrId = g}} pk = do
serverKey <- case thAuth thParams of
Nothing -> throwE $ PCETransportError TENoServerAuth
Just THAuthClient {serverPeerPubKey = k} -> pure k
nonce <- liftIO . atomically $ C.randomCbNonce g
let dId = BA.convert $ C.pubKeyBytes $ C.publicKey pk
sendProtocolCommand_ c (Just nonce) Nothing Nothing dId (Cmd SSender READ) >>= \case
DATA encBlob -> decryptDataBlob serverKey pk nonce encBlob
r -> throwE $ unexpectedResponse r
proxyGetSMPDataBlob :: SMPClient -> ProxiedRelay -> BlobId -> ExceptT SMPClientError IO (Either ProxyClientError EncDataBlob)
proxyGetSMPDataBlob c proxiedRelay dId = proxySMPCommand c proxiedRelay Nothing dId READ $ \case
DATA encBlob -> Just encBlob
_ -> Nothing
proxyGetSMPDataBlob :: SMPClient -> ProxiedRelay -> C.PrivateKeyX25519 -> ExceptT SMPClientError IO (Either ProxyClientError DataBlob)
proxyGetSMPDataBlob c@ProtocolClient {client_ = PClient {clientCorrId = g}} proxiedRelay@ProxiedRelay {prServerKey} pk = do
nonce <- liftIO . atomically $ C.randomCbNonce g
let dId = BA.convert $ C.pubKeyBytes $ C.publicKey pk
encBlob_ <-
proxySMPCommand_ c (Just nonce) proxiedRelay Nothing dId READ $ \case
DATA encBlob -> Just encBlob
_ -> Nothing
bimapM pure (decryptDataBlob prServerKey pk nonce) encBlob_
decryptDataBlob :: C.PublicKeyX25519 -> C.PrivateKeyX25519 -> C.CbNonce -> ByteString -> ExceptT (ProtocolClientError ErrorType) IO DataBlob
decryptDataBlob serverKey pk nonce encBlob = do
let ss = C.dh' serverKey pk
blobStr <- liftEitherWith PCECryptoError $ C.cbDecrypt ss nonce encBlob
liftEitherWith (const $ PCEResponseError BLOCK) $ smpDecode blobStr
-- send PRXY :: SMPServer -> Maybe BasicAuth -> Command Sender
-- receives PKEY :: SessionId -> X.CertificateChain -> X.SignedExact X.PubKey -> BrokerMsg
@@ -905,6 +925,9 @@ instance StrEncoding ProxyClientError where
"SYNTAX" -> ProxyResponseError <$> _strP
_ -> fail "bad ProxyClientError"
proxySMPCommand :: SMPClient -> ProxiedRelay -> Maybe SndPrivateAuthKey -> SenderId -> Command 'Sender -> (BrokerMsg -> Maybe r) -> ExceptT SMPClientError IO (Either ProxyClientError r)
proxySMPCommand c = proxySMPCommand_ c Nothing
-- consider how to process slow responses - is it handled somehow locally or delegated to the caller
-- this method is used in the client
-- sends PFWD :: C.PublicKeyX25519 -> EncTransmission -> Command Sender
@@ -932,23 +955,25 @@ instance StrEncoding ProxyClientError where
-- - other errors from the client running on proxy and connected to relay in PREProxiedRelayError
-- This function proxies Sender commands that return OK or ERR
proxySMPCommand ::
proxySMPCommand_ ::
SMPClient ->
-- optional correlation ID/nonce for the sending client
Maybe C.CbNonce ->
-- proxy session from PKEY
ProxiedRelay ->
-- message to deliver
-- command to deliver
Maybe SndPrivateAuthKey ->
SenderId ->
Command 'Sender ->
(BrokerMsg -> Maybe r) ->
ExceptT SMPClientError IO (Either ProxyClientError r)
proxySMPCommand c@ProtocolClient {thParams = proxyThParams, client_ = PClient {clientCorrId = g, tcpTimeout}} (ProxiedRelay sessionId v _ serverKey) spKey sId command toResult = do
proxySMPCommand_ c@ProtocolClient {thParams = proxyThParams, client_ = PClient {clientCorrId = g, tcpTimeout}} nonce_ (ProxiedRelay sessionId v _ serverKey) spKey sId command toResult = do
-- prepare params
let serverThAuth = (\ta -> ta {serverPeerPubKey = serverKey}) <$> thAuth proxyThParams
serverThParams = smpTHParamsSetVersion v proxyThParams {sessionId, thAuth = serverThAuth}
(cmdPubKey, cmdPrivKey) <- liftIO . atomically $ C.generateKeyPair @'C.X25519 g
let cmdSecret = C.dh' serverKey cmdPrivKey
nonce@(C.CbNonce corrId) <- liftIO . atomically $ C.randomCbNonce g
nonce@(C.CbNonce corrId) <- liftIO $ maybe (atomically $ C.randomCbNonce g) pure nonce_
-- encode
let TransmissionForAuth {tForAuth, tToSend} = encodeTransmissionForAuth serverThParams (CorrId corrId, sId, Cmd SSender command)
auth <- liftEitherWith PCETransportError $ authTransmission serverThAuth spKey nonce tForAuth
+10 -3
View File
@@ -47,6 +47,7 @@ module Simplex.Messaging.Transport
authCmdsSMPVersion,
sendingProxySMPVersion,
sndAuthKeySMPVersion,
dataBlobSMPVersion,
simplexMQVersion,
smpBlockSize,
TransportConfig (..),
@@ -130,6 +131,9 @@ smpBlockSize = 16384
-- 5 - basic auth for SMP servers (11/12/2022)
-- 6 - allow creating queues without subscribing (9/10/2023)
-- 7 - support authenticated encryption to verify senders' commands, imply but do NOT send session ID in signed part (4/30/2024)
-- 8 - forwarding proxy protecting IP addresses and sessions of command senders (5/14/2024)
-- 9 - securing message queue by sender (SKEY command) for faster connection handshake (6/30/2024)
-- 10 - storing data blobs on SMP servers for short invitation links (7/25/2024)
data SMPVersion
@@ -160,14 +164,17 @@ sendingProxySMPVersion = VersionSMP 8
sndAuthKeySMPVersion :: VersionSMP
sndAuthKeySMPVersion = VersionSMP 9
dataBlobSMPVersion :: VersionSMP
dataBlobSMPVersion = VersionSMP 10
currentClientSMPRelayVersion :: VersionSMP
currentClientSMPRelayVersion = VersionSMP 9
currentClientSMPRelayVersion = VersionSMP 10
legacyServerSMPRelayVersion :: VersionSMP
legacyServerSMPRelayVersion = VersionSMP 6
currentServerSMPRelayVersion :: VersionSMP
currentServerSMPRelayVersion = VersionSMP 9
currentServerSMPRelayVersion = VersionSMP 10
-- Max SMP protocol version to be used in e2e encrypted
-- connection between client and server, as defined by SMP proxy.
@@ -175,7 +182,7 @@ currentServerSMPRelayVersion = VersionSMP 9
-- to prevent client version fingerprinting by the
-- destination relays when clients upgrade at different times.
proxiedSMPRelayVersion :: VersionSMP
proxiedSMPRelayVersion = VersionSMP 9
proxiedSMPRelayVersion = VersionSMP 10
-- minimal supported protocol version is 4
-- TODO remove code that supports sending commands without batching