Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remote: add multicast discovery w/ encrypted announce #895

Merged
merged 8 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 15 additions & 16 deletions src/Simplex/Messaging/Agent.hs
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@
xftpDeleteSndFileRemote,
rcNewHostPairing,
rcConnectHost,
rcConnectCtrlURI,
rcConnectCtrlMulticast,
rcConnectCtrl,
rcDiscoverCtrl,
foregroundAgent,
suspendAgent,
execAgentStoreSQL,
Expand Down Expand Up @@ -317,7 +317,7 @@
setProtocolServers c = withAgentEnv c .: setProtocolServers' c

-- | Test protocol server
testProtocolServer :: forall p m. (ProtocolTypeI p, UserProtocol p, AgentErrorMonad m) => AgentClient -> UserId -> ProtoServerWithAuth p -> m (Maybe ProtocolTestFailure)

Check warning on line 320 in src/Simplex/Messaging/Agent.hs

View workflow job for this annotation

GitHub Actions / build-ubuntu-20.04

Redundant constraint: UserProtocol p

Check warning on line 320 in src/Simplex/Messaging/Agent.hs

View workflow job for this annotation

GitHub Actions / build-ubuntu-22.04

Redundant constraint: UserProtocol p
testProtocolServer c userId srv = withAgentEnv c $ case protocolTypeI @p of
SPSMP -> runSMPServerTest c userId srv
SPXFTP -> runXFTPServerTest c userId srv
Expand Down Expand Up @@ -396,28 +396,27 @@
rcConnectHost c = withAgentEnv c .:. rcConnectHost'

rcConnectHost' :: AgentMonad m => RCHostPairing -> J.Value -> Bool -> m RCHostConnection
rcConnectHost' pairing ctrlAppInfo _multicast = do
rcConnectHost' pairing ctrlAppInfo multicast = do
drg <- asks random
liftError RCP $ connectRCHost drg pairing ctrlAppInfo
liftError RCP $ connectRCHost drg pairing ctrlAppInfo multicast

-- | connect to remote controller via URI
rcConnectCtrlURI :: AgentErrorMonad m => AgentClient -> RCSignedInvitation -> Maybe RCCtrlPairing -> J.Value -> m RCCtrlConnection
rcConnectCtrlURI c = withAgentEnv c .:. rcConnectCtrlURI'
rcConnectCtrl :: AgentErrorMonad m => AgentClient -> RCVerifiedInvitation -> Maybe RCCtrlPairing -> J.Value -> m RCCtrlConnection
rcConnectCtrl c = withAgentEnv c .:. rcConnectCtrl'

rcConnectCtrlURI' :: AgentMonad m => RCSignedInvitation -> Maybe RCCtrlPairing -> J.Value -> m RCCtrlConnection
rcConnectCtrlURI' signedInv pairing_ hostAppInfo = do
rcConnectCtrl' :: AgentMonad m => RCVerifiedInvitation -> Maybe RCCtrlPairing -> J.Value -> m RCCtrlConnection
rcConnectCtrl' verifiedInv pairing_ hostAppInfo = do
drg <- asks random
liftError RCP $ connectRCCtrlURI drg signedInv pairing_ hostAppInfo
liftError RCP $ connectRCCtrl drg verifiedInv pairing_ hostAppInfo

-- | connect to known remote controller via multicast
rcConnectCtrlMulticast :: AgentErrorMonad m => AgentClient -> NonEmpty RCCtrlPairing -> J.Value -> m RCCtrlConnection
rcConnectCtrlMulticast c = withAgentEnv c .: rcConnectCtrlMulticast'
rcDiscoverCtrl :: AgentErrorMonad m => AgentClient -> NonEmpty RCCtrlPairing -> m (RCCtrlPairing, RCVerifiedInvitation)
rcDiscoverCtrl c = withAgentEnv c . rcDiscoverCtrl'

rcConnectCtrlMulticast' :: AgentMonad m => NonEmpty RCCtrlPairing -> J.Value -> m RCCtrlConnection
rcConnectCtrlMulticast' pairings hostAppInfo = do
drg <- asks random
subscribers <- newTVarIO 0 -- TODO: get from agent
liftError RCP $ connectKnownRCCtrlMulticast drg subscribers pairings hostAppInfo
rcDiscoverCtrl' :: AgentMonad m => NonEmpty RCCtrlPairing -> m (RCCtrlPairing, RCVerifiedInvitation)
rcDiscoverCtrl' pairings = do
subs <- asks multicastSubscribers
liftError RCP $ discoverRCCtrl subs pairings

-- | Activate operations
foregroundAgent :: MonadUnliftIO m => AgentClient -> m ()
Expand Down
6 changes: 4 additions & 2 deletions src/Simplex/Messaging/Agent/Env/SQLite.hs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,8 @@ data Env = Env
clientCounter :: TVar Int,
randomServer :: TVar StdGen,
ntfSupervisor :: NtfSupervisor,
xftpAgent :: XFTPAgent
xftpAgent :: XFTPAgent,
multicastSubscribers :: TMVar Int
}

newSMPAgentEnv :: AgentConfig -> SQLiteStore -> IO Env
Expand All @@ -192,7 +193,8 @@ newSMPAgentEnv config@AgentConfig {initialClientId} store = do
randomServer <- newTVarIO =<< liftIO newStdGen
ntfSupervisor <- atomically . newNtfSubSupervisor $ tbqSize config
xftpAgent <- atomically newXFTPAgent
pure Env {config, store, random, clientCounter, randomServer, ntfSupervisor, xftpAgent}
multicastSubscribers <- newTMVarIO 0
pure Env {config, store, random, clientCounter, randomServer, ntfSupervisor, xftpAgent, multicastSubscribers}

createAgentStore :: FilePath -> String -> MigrationConfirmation -> IO (Either MigrationError SQLiteStore)
createAgentStore dbFilePath dbKey = createSQLiteStore dbFilePath dbKey Migrations.app
Expand Down
4 changes: 4 additions & 0 deletions src/Simplex/Messaging/Util.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import qualified Data.Text as T
import Data.Text.Encoding (decodeUtf8With)
import Data.Time (NominalDiffTime)
import GHC.Conc
import UnliftIO
import UnliftIO.Async
import qualified UnliftIO.Exception as UE

Expand Down Expand Up @@ -136,6 +137,9 @@ safeDecodeUtf8 = decodeUtf8With onError
where
onError _ _ = Just '?'

timeoutThrow :: (MonadUnliftIO m, MonadError e m) => e -> Int -> m a -> m a
timeoutThrow e ms action = timeout ms action >>= maybe (throwError e) pure

threadDelay' :: Int64 -> IO ()
threadDelay' time
| time <= 0 = pure ()
Expand Down
95 changes: 62 additions & 33 deletions src/Simplex/RemoteControl/Client.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ module Simplex.RemoteControl.Client
cancelHostClient,
RCCtrlClient (action),
RCCtrlConnection,
connectRCCtrlURI,
connectKnownRCCtrlMulticast,
connectRCCtrl,
discoverRCCtrl,
confirmCtrlSession,
cancelCtrlClient,
RCStepTMVar,
Expand Down Expand Up @@ -45,8 +45,9 @@ import qualified Data.Text as T
import Data.Time.Clock.System (getSystemTime)
import qualified Data.X509 as X509
import Data.X509.Validation (Fingerprint (..), getFingerprint)
import Network.Socket (PortNumber)
import Network.Socket (SockAddr (..), PortNumber, hostAddressToTuple)
import qualified Network.TLS as TLS
import qualified Network.UDP as UDP
import Simplex.Messaging.Agent.Client ()
import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.Crypto.Lazy (LazyByteString)
Expand All @@ -55,14 +56,15 @@ import Simplex.Messaging.Crypto.SNTRUP761
import Simplex.Messaging.Crypto.SNTRUP761.Bindings
import Simplex.Messaging.Encoding
import Simplex.Messaging.Transport (TLS (tlsUniq), cGet, cPut)
import Simplex.Messaging.Transport.Client (TransportClientConfig (..), TransportHost, defaultTransportClientConfig, runTransportClient)
import Simplex.Messaging.Transport.Client (TransportClientConfig (..), TransportHost (..), defaultTransportClientConfig, runTransportClient)
import Simplex.Messaging.Transport.Credentials (genCredentials, tlsCredentials)
import Simplex.Messaging.Util
import Simplex.Messaging.Version
import Simplex.RemoteControl.Discovery (getLocalAddress, startTLSServer)
import Simplex.RemoteControl.Discovery (getLocalAddress, recvAnnounce, startTLSServer, withListener, withSender)
import Simplex.RemoteControl.Invitation
import Simplex.RemoteControl.Types
import UnliftIO
import UnliftIO.Concurrent

currentRCVersion :: Version
currentRCVersion = 1
Expand All @@ -76,6 +78,9 @@ xrcpBlockSize = 16384
helloBlockSize :: Int
helloBlockSize = 12288

announceBlockSize :: Int
announceBlockSize = 1024 -- TODO: put a real number
dpwiz marked this conversation as resolved.
Show resolved Hide resolved

newRCHostPairing :: IO RCHostPairing
newRCHostPairing = do
((_, caKey), caCert) <- genCredentials Nothing (-25, 24 * 999999) "ca"
Expand All @@ -89,33 +94,40 @@ data RCHostClient = RCHostClient

data RCHClient_ = RCHClient_
{ startedPort :: TMVar (Maybe PortNumber),
announcer :: TMVar (Async (Either RCErrorType ())),
hostCAHash :: TMVar C.KeyHash,
endSession :: TMVar ()
}

type RCHostConnection = (RCSignedInvitation, RCHostClient, RCStepTMVar (SessionCode, TLS, RCStepTMVar (RCHostSession, RCHostHello, RCHostPairing)))

connectRCHost :: TVar ChaChaDRG -> RCHostPairing -> J.Value -> ExceptT RCErrorType IO RCHostConnection
connectRCHost drg pairing@RCHostPairing {caKey, caCert, idPrivKey, knownHost} ctrlAppInfo = do
connectRCHost :: TVar ChaChaDRG -> RCHostPairing -> J.Value -> Bool -> ExceptT RCErrorType IO RCHostConnection
connectRCHost drg pairing@RCHostPairing {caKey, caCert, idPrivKey, knownHost} ctrlAppInfo multicast = do
r <- newEmptyTMVarIO
host <- getLocalAddress >>= maybe (throwError RCENoLocalAddress) pure
c@RCHClient_ {startedPort} <- liftIO mkClient
c@RCHClient_ {startedPort, announcer} <- liftIO mkClient
hostKeys <- liftIO genHostKeys
action <- runClient c r hostKeys `putRCError` r
-- wait for the port to make invitation
-- TODO can't we actually find to which interface the server got connected to get host there?
portNum <- atomically $ readTMVar startedPort
signedInv <- maybe (throwError RCETLSStartFailed) (liftIO . mkInvitation hostKeys host) portNum
signedInv@RCSignedInvitation{invitation} <- maybe (throwError RCETLSStartFailed) (liftIO . mkInvitation hostKeys host) portNum
when multicast $ case knownHost of
Nothing -> fail "oops, must have known host for multicast"
dpwiz marked this conversation as resolved.
Show resolved Hide resolved
Just KnownHostPairing {hostDhPubKey} -> do
ann <- async . liftIO . runExceptT $ announceRC drg 60 idPrivKey hostDhPubKey hostKeys invitation
atomically $ putTMVar announcer ann
pure (signedInv, RCHostClient {action, client_ = c}, r)
where
mkClient :: IO RCHClient_
mkClient = do
startedPort <- newEmptyTMVarIO
announcer <- newEmptyTMVarIO
endSession <- newEmptyTMVarIO
hostCAHash <- newEmptyTMVarIO
pure RCHClient_ {startedPort, hostCAHash, endSession}
pure RCHClient_ {startedPort, announcer, hostCAHash, endSession}
runClient :: RCHClient_ -> RCStepTMVar (SessionCode, TLS, RCStepTMVar (RCHostSession, RCHostHello, RCHostPairing)) -> RCHostKeys -> ExceptT RCErrorType IO (Async ())
runClient RCHClient_ {startedPort, hostCAHash, endSession} r hostKeys = do
runClient RCHClient_ {startedPort, announcer, hostCAHash, endSession} r hostKeys = do
tlsCreds <- liftIO $ genTLSCredentials caKey caCert
startTLSServer startedPort tlsCreds (tlsHooks r knownHost hostCAHash) $ \tls ->
void . runExceptT $ do
Expand All @@ -132,6 +144,7 @@ connectRCHost drg pairing@RCHostPairing {caKey, caCert, idPrivKey, knownHost} ct
sendRCPacket tls ctrlEncHello
logDebug "Sent ctrl HELLO"
whenM (atomically $ tryPutTMVar r' $ Right (RCHostSession {tls, sessionKeys}, helloBody, pairing')) $ do
atomically (tryReadTMVar announcer) >>= mapM_ uninterruptibleCancel
-- can use `RCHostSession` until `endSession` is signalled
logDebug "Holding session"
atomically $ takeTMVar endSession
Expand Down Expand Up @@ -185,8 +198,9 @@ certFingerprint caCert = C.KeyHash fp
Fingerprint fp = getFingerprint caCert X509.HashSHA256

cancelHostClient :: RCHostClient -> IO ()
cancelHostClient RCHostClient {action, client_ = RCHClient_ {endSession}} = do
cancelHostClient RCHostClient {action, client_ = RCHClient_ {announcer, endSession}} = do
atomically $ putTMVar endSession ()
atomically (tryTakeTMVar announcer) >>= mapM_ uninterruptibleCancel
uninterruptibleCancel action

prepareHostSession :: TVar ChaChaDRG -> C.KeyHash -> RCHostPairing -> RCHostKeys -> RCHostEncHello -> ExceptT RCErrorType IO (RCCtrlEncHello, HostSessKeys, RCHostHello, RCHostPairing)
Expand Down Expand Up @@ -232,14 +246,9 @@ data RCCClient_ = RCCClient_

type RCCtrlConnection = (RCCtrlClient, RCStepTMVar (SessionCode, TLS, RCStepTMVar (RCCtrlSession, RCCtrlPairing)))

connectRCCtrlURI :: TVar ChaChaDRG -> RCSignedInvitation -> Maybe RCCtrlPairing -> J.Value -> ExceptT RCErrorType IO RCCtrlConnection
connectRCCtrlURI drg signedInv@RCSignedInvitation {invitation} pairing_ hostAppInfo = do
unless (verifySignedInviteURI signedInv) $ throwError RCECtrlAuth
connectRCCtrl drg invitation pairing_ hostAppInfo

-- app should determine whether it is a new or known pairing based on CA fingerprint in the invitation
connectRCCtrl :: TVar ChaChaDRG -> RCInvitation -> Maybe RCCtrlPairing -> J.Value -> ExceptT RCErrorType IO RCCtrlConnection
connectRCCtrl drg inv@RCInvitation {ca, idkey} pairing_ hostAppInfo = do
connectRCCtrl :: TVar ChaChaDRG -> RCVerifiedInvitation -> Maybe RCCtrlPairing -> J.Value -> ExceptT RCErrorType IO RCCtrlConnection
connectRCCtrl drg (RCVerifiedInvitation inv@RCInvitation {ca, idkey}) pairing_ hostAppInfo = do
pairing' <- maybe (liftIO newCtrlPairing) updateCtrlPairing pairing_
connectRCCtrl_ drg pairing' inv hostAppInfo
where
Expand Down Expand Up @@ -349,24 +358,44 @@ prepareCtrlSession
message <- liftEitherWith (const RCEDecrypt) $ C.cbDecrypt sharedKey nonce encMessage
throwError $ RCECtrlError $ T.unpack $ safeDecodeUtf8 message

announceRC :: TVar ChaChaDRG -> Int -> C.PrivateKeyEd25519 -> C.PublicKeyX25519 -> RCHostKeys -> RCInvitation -> ExceptT RCErrorType IO ()
announceRC drg maxCount idPrivKey knownDhPub RCHostKeys {sessKeys, dhKeys} inv = withSender $ \sender -> do
replicateM_ maxCount $ do
nonce <- atomically $ C.pseudoRandomCbNonce drg
let sharedKey = C.dh' knownDhPub dhPrivKey
encInvitation <- liftEitherWith undefined $ C.cbEncrypt sharedKey nonce signedSA announceBlockSize
logDebug "Announcing..."
liftIO . UDP.send sender $ smpEncode RCEncInvitation {dhPubKey, nonce, encInvitation}
threadDelay 1000000
where
signedSA = signInviteJSON sPrivKey idPrivKey inv
(_sPub, sPrivKey) = sessKeys
(dhPubKey, dhPrivKey) = dhKeys

-- The application should save updated RCHostPairing after user confirmation of the session
-- TMVar resolves when TLS is connected
connectKnownRCCtrlMulticast :: TVar ChaChaDRG -> TVar Int -> NonEmpty RCCtrlPairing -> J.Value -> ExceptT RCErrorType IO RCCtrlConnection
connectKnownRCCtrlMulticast drg _subscribers pairings hostAppInfo = do
-- start multicast
-- receive packets
let loop = undefined -- catch and log errors, fail on timeout
receive = undefined
parse = undefined
(pairing, inv) <- loop $ receive >>= parse >>= findRCCtrlPairing pairings
connectRCCtrl drg inv pairing hostAppInfo

findRCCtrlPairing :: NonEmpty RCCtrlPairing -> RCEncInvitation -> ExceptT RCErrorType IO (RCCtrlPairing, RCInvitation)
discoverRCCtrl :: TMVar Int -> NonEmpty RCCtrlPairing -> ExceptT RCErrorType IO (RCCtrlPairing, RCVerifiedInvitation)
discoverRCCtrl subscribers pairings =
timeoutThrow RCENotDiscovered 30000000 $ withListener subscribers $ \listener ->
loop $ do
(source, bytes) <- recvAnnounce listener
encInvitation <- liftEitherWith RCESyntax $ smpDecode bytes
r@(_, RCVerifiedInvitation RCInvitation {host}) <- findRCCtrlPairing pairings encInvitation
case source of
SockAddrInet _ ha | THIPv4 (hostAddressToTuple ha) == host -> pure ()
_ -> throwError RCEAddress
pure r
where
loop :: ExceptT RCErrorType IO a -> ExceptT RCErrorType IO a
loop action = liftIO (runExceptT action) >>= \case
Left err -> logError (tshow err) >> loop action
Right res -> pure res

findRCCtrlPairing :: NonEmpty RCCtrlPairing -> RCEncInvitation -> ExceptT RCErrorType IO (RCCtrlPairing, RCVerifiedInvitation)
findRCCtrlPairing pairings RCEncInvitation {dhPubKey, nonce, encInvitation} = do
(pairing, signedInvStr) <- liftEither $ decrypt (L.toList pairings)
signedInv@RCSignedInvitation {invitation} <- liftEitherWith RCESyntax $ smpDecode signedInvStr
unless (verifySignedInvitationMulticast signedInv) $ throwError RCECtrlAuth
pure (pairing, invitation)
signedInv <- liftEitherWith RCESyntax $ smpDecode signedInvStr
maybe (throwError RCECtrlAuth) (pure . (pairing,)) $ verifySignedInvitationMulticast signedInv
where
decrypt :: [RCCtrlPairing] -> Either RCErrorType (RCCtrlPairing, ByteString)
decrypt [] = Left RCECtrlNotFound
Expand Down
85 changes: 66 additions & 19 deletions src/Simplex/RemoteControl/Invitation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

module Simplex.RemoteControl.Invitation where

import Control.Monad
import qualified Crypto.PubKey.Ed25519 as Ed25519
import qualified Data.Aeson as J
import qualified Data.Aeson.TH as JQ
import qualified Data.Attoparsec.ByteString.Char8 as A
Expand Down Expand Up @@ -133,25 +135,6 @@ signInviteURL sKey idKey invitation = RCSignedInvitation {invitation, ssig, idsi
C.ASignature C.SEd25519 s -> s
_ -> error "signing with ed25519"

verifySignedInviteURI :: RCSignedInvitation -> Bool
verifySignedInviteURI RCSignedInvitation {invitation, ssig, idsig} =
C.verify aSKey aSSig inviteURL && C.verify aIdKey aIdSig inviteURLS
where
RCInvitation {skey, idkey} = invitation
inviteURL = strEncode invitation
inviteURLS = mconcat [inviteURL, "&ssig=", strEncode ssig]
aSKey = C.APublicVerifyKey C.SEd25519 skey
aSSig = C.ASignature C.SEd25519 ssig
aIdKey = C.APublicVerifyKey C.SEd25519 idkey
aIdSig = C.ASignature C.SEd25519 idsig

instance Encoding RCSignedInvitation where
smpEncode RCSignedInvitation {} = error "TODO: RCSignedInvitation.smpEncode"
smpP = error "TODO: RCSignedInvitation.smpP"

verifySignedInvitationMulticast :: RCSignedInvitation -> Bool
verifySignedInvitationMulticast RCSignedInvitation {invitation, ssig, idsig} = undefined

data RCEncInvitation = RCEncInvitation
{ dhPubKey :: C.PublicKeyX25519,
nonce :: C.CbNonce,
Expand All @@ -174,3 +157,67 @@ requiredP q k f = maybe (fail $ "missing " <> show k) (either fail pure . f) $ l
-- optionalP q k f = maybe (pure Nothing) (either fail (pure . Just) . f) $ lookup k q

$(JQ.deriveJSON defaultJSON ''RCInvitation)

sessionAddressJSON :: RCInvitation -> ByteString
sessionAddressJSON = LB.toStrict . J.encode . J.toJSON

signInviteJSON :: C.PrivateKey C.Ed25519 -> C.PrivateKey C.Ed25519 -> RCInvitation -> ByteString
signInviteJSON sKey idKey invitation = idSigned
where
sessionAddress = sessionAddressJSON invitation

ssig :: C.Signature 'C.Ed25519
ssig =
case C.sign (C.APrivateSignKey C.SEd25519 sKey) sessionAddress of
dpwiz marked this conversation as resolved.
Show resolved Hide resolved
C.ASignature C.SEd25519 s -> s
_ -> error "signing with ed25519"
sSigned = sessionAddress <> C.signatureBytes ssig

idsig :: C.Signature 'C.Ed25519
idsig =
case C.sign (C.APrivateSignKey C.SEd25519 idKey) sSigned of
C.ASignature C.SEd25519 s -> s
_ -> error "signing with ed25519"
dpwiz marked this conversation as resolved.
Show resolved Hide resolved
idSigned = sSigned <> C.signatureBytes idsig

instance Encoding RCSignedInvitation where
smpEncode RCSignedInvitation {invitation, ssig, idsig} = sessionAddressJSON invitation <> C.signatureBytes ssig <> C.signatureBytes idsig
dpwiz marked this conversation as resolved.
Show resolved Hide resolved
smpDecode bs = do
let (json, sigs) = B.splitAt sigStart bs
unless (B.length sigs == sigLen * 2) $ Left "bad size"
invitation <- J.eitherDecodeStrict json
let (ssig, idsig) = B.splitAt sigLen sigs
RCSignedInvitation invitation <$> C.decodeSignature ssig <*> C.decodeSignature idsig
where
sigStart = B.length bs - 2 * sigLen
sigLen = Ed25519.signatureSize

newtype RCVerifiedInvitation = RCVerifiedInvitation RCInvitation

verifySignedInviteURI :: RCSignedInvitation -> Maybe RCVerifiedInvitation
verifySignedInviteURI RCSignedInvitation {invitation, ssig, idsig} =
if C.verify aSKey aSSig inviteURL && C.verify aIdKey aIdSig inviteURLS
then Just $ RCVerifiedInvitation invitation
else Nothing
where
RCInvitation {skey, idkey} = invitation
inviteURL = strEncode invitation
inviteURLS = mconcat [inviteURL, "&ssig=", strEncode ssig]
aSKey = C.APublicVerifyKey C.SEd25519 skey
aSSig = C.ASignature C.SEd25519 ssig
aIdKey = C.APublicVerifyKey C.SEd25519 idkey
aIdSig = C.ASignature C.SEd25519 idsig

verifySignedInvitationMulticast :: RCSignedInvitation -> Maybe RCVerifiedInvitation
verifySignedInvitationMulticast RCSignedInvitation {invitation, ssig, idsig} =
if C.verify aSKey aSSig sa && C.verify aIdKey aIdSig sSigned
then Just $ RCVerifiedInvitation invitation
else Nothing
where
RCInvitation {skey, idkey} = invitation
sa = sessionAddressJSON invitation
sSigned = sa <> C.signatureBytes ssig
aSKey = C.APublicVerifyKey C.SEd25519 skey
aSSig = C.ASignature C.SEd25519 ssig
aIdKey = C.APublicVerifyKey C.SEd25519 idkey
aIdSig = C.ASignature C.SEd25519 idsig
Loading
Loading