mobile: webrtc calls (#661)

* mobile: webrtc calls work on Android and iOS separately

* core: only send public key with offer if invitation/current call supports e2ee

* update npm module
This commit is contained in:
Evgeny Poberezkin
2022-05-18 07:01:32 +01:00
committed by GitHub
parent 106dceabfc
commit dc418923ac
20 changed files with 361 additions and 155 deletions
+7 -8
View File
@@ -430,13 +430,13 @@ processChatCommand = \case
`E.finally` deleteContactRequest st userId connReqId
withAgent $ \a -> rejectContact a connId invId
pure $ CRContactRequestRejected cReq
APISendCallInvitation contactId callType@CallType {capabilities = CallCapabilities {encryption}} -> withUser $ \user@User {userId} -> do
APISendCallInvitation contactId callType -> withUser $ \user@User {userId} -> do
-- party initiating call
ct <- withStore $ \st -> getContact st userId contactId
calls <- asks currentCalls
withChatLock $ do
callId <- CallId <$> (asks idsDrg >>= liftIO . (`randomBytes` 16))
dhKeyPair <- if encryption then Just <$> liftIO C.generateKeyPair' else pure Nothing
dhKeyPair <- if encryptedCall callType then Just <$> liftIO C.generateKeyPair' else pure Nothing
let invitation = CallInvitation {callType, callDhPubKey = fst <$> dhKeyPair}
callState = CallInvitationSent {localCallType = callType, localDhPrivKey = snd <$> dhKeyPair}
msg@SndMessage {msgId} <- sendDirectContactMessage ct (XCallInv callId invitation)
@@ -460,8 +460,8 @@ processChatCommand = \case
-- party accepting call
withCurrentCall contactId $ \userId ct call@Call {callId, chatItemId, callState} -> case callState of
CallInvitationReceived {peerCallType, localDhPubKey, sharedKey} -> do
-- TODO check that call type matches peerCallType
let offer = CallOffer {callType, rtcSession, callDhPubKey = localDhPubKey}
let callDhPubKey = if encryptedCall callType then localDhPubKey else Nothing
offer = CallOffer {callType, rtcSession, callDhPubKey}
callState' = CallOfferSent {localCallType = callType, peerCallType, localCallSession = rtcSession, sharedKey}
aciContent = ACIContent SMDRcv $ CIRcvCall CISCallAccepted 0
SndMessage {msgId} <- sendDirectContactMessage ct (XCallOffer callId offer)
@@ -1630,8 +1630,7 @@ processAgentMessage (Just user@User {userId, profile}) agentConnId agentMessage
xCallInv :: Contact -> CallId -> CallInvitation -> RcvMessage -> MsgMeta -> m ()
xCallInv ct@Contact {contactId} callId CallInvitation {callType, callDhPubKey} msg@RcvMessage {msgId} msgMeta = do
checkIntegrity msgMeta $ toView . CRMsgIntegrityError
let CallType {capabilities = CallCapabilities {encryption}} = callType
dhKeyPair <- if encryption then Just <$> liftIO C.generateKeyPair' else pure Nothing
dhKeyPair <- if encryptedCall callType then Just <$> liftIO C.generateKeyPair' else pure Nothing
ci <- saveCallItem CISCallPending
let sharedKey = C.Key . C.dhBytes' <$> (C.dh' <$> callDhPubKey <*> (snd <$> dhKeyPair))
callState = CallInvitationReceived {peerCallType = callType, localDhPubKey = fst <$> dhKeyPair, sharedKey}
@@ -1655,8 +1654,8 @@ processAgentMessage (Just user@User {userId, profile}) agentConnId agentMessage
CallInvitationSent {localCallType, localDhPrivKey} -> do
let sharedKey = C.Key . C.dhBytes' <$> (C.dh' <$> callDhPubKey <*> localDhPrivKey)
callState' = CallOfferReceived {localCallType, peerCallType = callType, peerCallSession = rtcSession, sharedKey}
-- TODO decide if should askConfirmation
toView CRCallOffer {contact = ct, callType, offer = rtcSession, sharedKey, askConfirmation = False}
askConfirmation = encryptedCall localCallType && not (encryptedCall callType)
toView CRCallOffer {contact = ct, callType, offer = rtcSession, sharedKey, askConfirmation}
pure (Just call {callState = callState'}, Just . ACIContent SMDSnd $ CISndCall CISCallAccepted 0)
_ -> do
msgCallStateError "x.call.offer" call
+4
View File
@@ -3,6 +3,7 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
module Simplex.Chat.Call where
@@ -99,6 +100,9 @@ data CallType = CallType
defaultCallType :: CallType
defaultCallType = CallType CMVideo $ CallCapabilities {encryption = True}
encryptedCall :: CallType -> Bool
encryptedCall CallType {capabilities = CallCapabilities {encryption}} = encryption
instance ToJSON CallType where toEncoding = J.genericToEncoding J.defaultOptions
-- | * Types for chat protocol
+8 -7
View File
@@ -646,7 +646,7 @@ fileProgress chunksNum chunkSize fileSize =
viewCallInvitation :: Contact -> CallType -> Maybe C.Key -> [StyledString]
viewCallInvitation ct@Contact {contactId} callType@CallType {media} sharedKey =
[ ttyContact' ct <> " wants to connect with you via WebRTC " <> callMediaStr callType <> " call " <> encryptedCall callType,
[ ttyContact' ct <> " wants to connect with you via WebRTC " <> callMediaStr callType <> " call " <> encryptedCallText callType,
"To accept the call, please open the link below in your browser" <> supporedBrowsers callType,
"",
"https://simplex.chat/call#" <> plain queryString
@@ -662,7 +662,7 @@ viewCallInvitation ct@Contact {contactId} callType@CallType {media} sharedKey =
viewCallOffer :: Contact -> CallType -> WebRTCSession -> Maybe C.Key -> [StyledString]
viewCallOffer ct@Contact {contactId} callType@CallType {media} WebRTCSession {rtcSession = offer, rtcIceCandidates = iceCandidates} sharedKey =
[ ttyContact' ct <> " accepted your WebRTC " <> callMediaStr callType <> " call " <> encryptedCall callType,
[ ttyContact' ct <> " accepted your WebRTC " <> callMediaStr callType <> " call " <> encryptedCallText callType,
"To connect, please open the link below in your browser" <> supporedBrowsers callType,
"",
"https://simplex.chat/call#" <> plain queryString
@@ -689,13 +689,14 @@ callMediaStr CallType {media} = case media of
CMVideo -> "video"
CMAudio -> "audio"
encryptedCall :: CallType -> StyledString
encryptedCall CallType {capabilities = CallCapabilities {encryption}} =
if encryption then "(e2e encrypted)" else "(not e2e encrypted)"
encryptedCallText :: CallType -> StyledString
encryptedCallText callType
| encryptedCall callType = "(e2e encrypted)"
| otherwise = "(not e2e encrypted)"
supporedBrowsers :: CallType -> StyledString
supporedBrowsers CallType {capabilities = CallCapabilities {encryption}}
| encryption = " (only Chrome and Safari support e2e encryption for WebRTC, Safari requires enabling WebRTC insertable streams)"
supporedBrowsers callType
| encryptedCall callType = " (only Chrome and Safari support e2e encryption for WebRTC, Safari may require enabling WebRTC insertable streams)"
| otherwise = ""
data WCallCommand