mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 11:08:06 +00:00
ZRTP Changes
This commit is contained in:
parent
eca85b80ad
commit
7fb63c19dd
3 changed files with 124 additions and 110 deletions
|
|
@ -210,18 +210,6 @@ struct CallView: View {
|
|||
ZStack {
|
||||
VStack {
|
||||
if !fullscreenVideo || (fullscreenVideo && telecomManager.isPausedByRemote) {
|
||||
if #available(iOS 16.0, *) {
|
||||
Rectangle()
|
||||
.foregroundColor(Color.orangeMain500)
|
||||
.edgesIgnoringSafeArea(.top)
|
||||
.frame(height: 0)
|
||||
} else if idiom != .pad && !(orientation == .landscapeLeft || orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) {
|
||||
Rectangle()
|
||||
.foregroundColor(Color.orangeMain500)
|
||||
.edgesIgnoringSafeArea(.top)
|
||||
.frame(height: 1)
|
||||
}
|
||||
ZStack {
|
||||
HStack {
|
||||
Button {
|
||||
|
|
|
|||
|
|
@ -27,11 +27,6 @@ struct ZRTPPopup: View {
|
|||
|
||||
@ObservedObject var callViewModel: CallViewModel
|
||||
|
||||
@State private var letters1: String = "AA"
|
||||
@State private var letters2: String = "BB"
|
||||
@State private var letters3: String = "CC"
|
||||
@State private var letters4: String = "DD"
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
VStack(alignment: .leading) {
|
||||
|
|
@ -46,7 +41,7 @@ struct ZRTPPopup: View {
|
|||
Spacer()
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Text(letters1)
|
||||
Text(callViewModel.letters1)
|
||||
.default_text_style(styleSize: 30)
|
||||
.frame(width: 60, height: 60)
|
||||
}
|
||||
|
|
@ -54,12 +49,12 @@ struct ZRTPPopup: View {
|
|||
.background(Color.grayMain2c200)
|
||||
.cornerRadius(40)
|
||||
.onTapGesture {
|
||||
callViewModel.lettersClicked(letters: letters1)
|
||||
callViewModel.updateZrtpSas(authTokenClicked: callViewModel.letters1)
|
||||
callViewModel.zrtpPopupDisplayed = false
|
||||
}
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Text(letters2)
|
||||
Text(callViewModel.letters2)
|
||||
.default_text_style(styleSize: 30)
|
||||
.frame(width: 60, height: 60)
|
||||
}
|
||||
|
|
@ -67,7 +62,7 @@ struct ZRTPPopup: View {
|
|||
.background(Color.grayMain2c200)
|
||||
.cornerRadius(40)
|
||||
.onTapGesture {
|
||||
callViewModel.lettersClicked(letters: letters2)
|
||||
callViewModel.updateZrtpSas(authTokenClicked: callViewModel.letters2)
|
||||
callViewModel.zrtpPopupDisplayed = false
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +74,7 @@ struct ZRTPPopup: View {
|
|||
Spacer()
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Text(letters3)
|
||||
Text(callViewModel.letters3)
|
||||
.default_text_style(styleSize: 30)
|
||||
.frame(width: 60, height: 60)
|
||||
}
|
||||
|
|
@ -87,12 +82,12 @@ struct ZRTPPopup: View {
|
|||
.background(Color.grayMain2c200)
|
||||
.cornerRadius(40)
|
||||
.onTapGesture {
|
||||
callViewModel.lettersClicked(letters: letters3)
|
||||
callViewModel.updateZrtpSas(authTokenClicked: callViewModel.letters3)
|
||||
callViewModel.zrtpPopupDisplayed = false
|
||||
}
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Text(letters4)
|
||||
Text(callViewModel.letters4)
|
||||
.default_text_style(styleSize: 30)
|
||||
.frame(width: 60, height: 60)
|
||||
}
|
||||
|
|
@ -100,7 +95,7 @@ struct ZRTPPopup: View {
|
|||
.background(Color.grayMain2c200)
|
||||
.cornerRadius(40)
|
||||
.onTapGesture {
|
||||
callViewModel.lettersClicked(letters: letters4)
|
||||
callViewModel.updateZrtpSas(authTokenClicked: callViewModel.letters4)
|
||||
callViewModel.zrtpPopupDisplayed = false
|
||||
}
|
||||
|
||||
|
|
@ -118,10 +113,12 @@ struct ZRTPPopup: View {
|
|||
.frame(maxWidth: .infinity)
|
||||
.padding(.bottom, 30)
|
||||
.onTapGesture {
|
||||
callViewModel.skipZrtpAuthentication()
|
||||
callViewModel.zrtpPopupDisplayed = false
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
callViewModel.updateZrtpSas(authTokenClicked: "")
|
||||
callViewModel.zrtpPopupDisplayed = false
|
||||
}, label: {
|
||||
Text("Letters don't match!")
|
||||
|
|
@ -149,30 +146,10 @@ struct ZRTPPopup: View {
|
|||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
||||
.position(x: geometry.size.width / 2, y: geometry.size.height / 2)
|
||||
.onAppear {
|
||||
|
||||
var random = SystemRandomNumberGenerator()
|
||||
let correctLetters = Int(random.next(upperBound: UInt32(4)))
|
||||
|
||||
letters1 = (correctLetters == 0) ? callViewModel.upperCaseAuthTokenToListen : self.randomAlphanumericString(2)
|
||||
letters2 = (correctLetters == 1) ? callViewModel.upperCaseAuthTokenToListen : self.randomAlphanumericString(2)
|
||||
letters3 = (correctLetters == 2) ? callViewModel.upperCaseAuthTokenToListen : self.randomAlphanumericString(2)
|
||||
letters4 = (correctLetters == 3) ? callViewModel.upperCaseAuthTokenToListen : self.randomAlphanumericString(2)
|
||||
callViewModel.remoteAuthenticationTokens()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func randomAlphanumericString(_ length: Int) -> String {
|
||||
let letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
let len = UInt32(letters.count)
|
||||
var random = SystemRandomNumberGenerator()
|
||||
var randomString = ""
|
||||
for _ in 0..<length {
|
||||
let randomIndex = Int(random.next(upperBound: len))
|
||||
let randomCharacter = letters[letters.index(letters.startIndex, offsetBy: randomIndex)]
|
||||
randomString.append(randomCharacter)
|
||||
}
|
||||
return randomString
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
|
|
|
|||
|
|
@ -70,6 +70,11 @@ class CallViewModel: ObservableObject {
|
|||
|
||||
private var callSuscriptions = Set<AnyCancellable?>()
|
||||
|
||||
@Published var letters1: String = "AA"
|
||||
@Published var letters2: String = "BB"
|
||||
@Published var letters3: String = "CC"
|
||||
@Published var letters4: String = "DD"
|
||||
|
||||
init() {
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .voiceChat, options: .allowBluetooth)
|
||||
|
|
@ -164,8 +169,9 @@ class CallViewModel: ObservableObject {
|
|||
let isPausedTmp = self.isCallPaused()
|
||||
let timeElapsedTmp = self.currentCall?.duration ?? 0
|
||||
|
||||
let authToken = self.currentCall!.authenticationToken
|
||||
let isDeviceTrusted = self.currentCall!.authenticationTokenVerified && authToken != nil
|
||||
let authToken = self.currentCall!.localAuthenticationToken
|
||||
let cacheMismatchFlag = self.currentCall!.zrtpCacheMismatchFlag
|
||||
let isDeviceTrusted = !cacheMismatchFlag && self.currentCall!.authenticationTokenVerified && authToken != nil
|
||||
let isRemoteDeviceTrustedTmp = self.telecomManager.callInProgress ? isDeviceTrusted : false
|
||||
|
||||
if self.currentCall != nil {
|
||||
|
|
@ -215,11 +221,9 @@ class CallViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
self.callSuscriptions.insert(self.currentCall!.publisher?.onEncryptionChanged?.postOnCoreQueue {(cbVal: (call: Call, on: Bool, authenticationToken: String?)) in
|
||||
DispatchQueue.main.async {
|
||||
_ = self.updateEncryption()
|
||||
if self.currentCall != nil {
|
||||
self.callMediaEncryptionModel.update(call: self.currentCall!)
|
||||
}
|
||||
self.updateEncryption()
|
||||
if self.currentCall != nil {
|
||||
self.callMediaEncryptionModel.update(call: self.currentCall!)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -231,6 +235,18 @@ class CallViewModel: ObservableObject {
|
|||
}
|
||||
})
|
||||
|
||||
|
||||
self.callSuscriptions.insert(
|
||||
self.currentCall!.publisher?.onAuthenticationTokenVerified?.postOnCoreQueue {(call: Call, verified: Bool) in
|
||||
Log.warn("[CallViewModel][ZRTPPopup] Notified that authentication token is \(verified ? "verified" : "not verified!")")
|
||||
|
||||
self.updateEncryption()
|
||||
if self.currentCall != nil {
|
||||
self.callMediaEncryptionModel.update(call: self.currentCall!)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
self.updateCallQualityIcon()
|
||||
}
|
||||
}
|
||||
|
|
@ -832,81 +848,114 @@ class CallViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
func lettersClicked(letters: String) {
|
||||
let verified = letters == self.upperCaseAuthTokenToListen
|
||||
func skipZrtpAuthentication() {
|
||||
Log.info(
|
||||
"[ZRTPPopup] User clicked on \(verified ? "right" : "wrong") letters"
|
||||
"[ZRTPPopup] User skipped SAS validation in ZRTP call"
|
||||
)
|
||||
|
||||
if verified {
|
||||
coreContext.doOnCoreQueue { core in
|
||||
if core.currentCall != nil {
|
||||
core.currentCall!.authenticationTokenVerified = verified
|
||||
}
|
||||
coreContext.doOnCoreQueue { core in
|
||||
if core.currentCall != nil {
|
||||
core.currentCall!.skipZrtpAuthentication()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateEncryption() -> Bool {
|
||||
if currentCall != nil && currentCall!.currentParams != nil {
|
||||
switch currentCall!.currentParams!.mediaEncryption {
|
||||
case MediaEncryption.ZRTP:
|
||||
let authToken = currentCall!.authenticationToken
|
||||
let isDeviceTrusted = currentCall!.authenticationTokenVerified && authToken != nil
|
||||
|
||||
Log.info(
|
||||
"[CallViewModel] Current call media encryption is ZRTP, auth token is \(isDeviceTrusted ? "trusted" : "not trusted yet")"
|
||||
)
|
||||
|
||||
isRemoteDeviceTrusted = isDeviceTrusted
|
||||
|
||||
if isDeviceTrusted {
|
||||
ToastViewModel.shared.toastMessage = "Info_call_securised"
|
||||
ToastViewModel.shared.displayToast = true
|
||||
func updateZrtpSas(authTokenClicked: String) {
|
||||
coreContext.doOnCoreQueue { core in
|
||||
if core.currentCall != nil {
|
||||
if authTokenClicked.isEmpty {
|
||||
Log.error(
|
||||
"[ZRTPPopup] Doing a fake ZRTP SAS check with empty token because user clicked on 'Not Found' button!"
|
||||
)
|
||||
} else {
|
||||
Log.info(
|
||||
"[ZRTPPopup] Checking if ZRTP SAS auth token \(authTokenClicked) is the right one"
|
||||
)
|
||||
}
|
||||
core.currentCall!.checkAuthenticationTokenSelected(selectedValue: authTokenClicked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func remoteAuthenticationTokens() {
|
||||
coreContext.doOnCoreQueue { core in
|
||||
if core.currentCall != nil {
|
||||
let tokens = core.currentCall!.remoteAuthenticationTokens
|
||||
self.letters1 = tokens[0]
|
||||
self.letters2 = tokens[1]
|
||||
self.letters3 = tokens[2]
|
||||
self.letters4 = tokens[3]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateEncryption() {
|
||||
coreContext.doOnCoreQueue { core in
|
||||
if self.currentCall != nil && self.currentCall!.currentParams != nil {
|
||||
switch self.currentCall!.currentParams!.mediaEncryption {
|
||||
case MediaEncryption.ZRTP:
|
||||
let authToken = self.currentCall!.localAuthenticationToken
|
||||
let isDeviceTrusted = self.currentCall!.authenticationTokenVerified && authToken != nil
|
||||
|
||||
Log.info(
|
||||
"[CallViewModel] Current call media encryption is ZRTP, auth token is \(isDeviceTrusted ? "trusted" : "not trusted yet")"
|
||||
)
|
||||
|
||||
let cacheMismatchFlag = self.currentCall!.zrtpCacheMismatchFlag
|
||||
let isRemoteDeviceTrustedTmp = !cacheMismatchFlag && isDeviceTrusted
|
||||
|
||||
/*
|
||||
let securityLevel = isDeviceTrusted ? SecurityLevel.Safe : SecurityLevel.Encrypted
|
||||
let avatarModel = contact
|
||||
if (avatarModel != nil) {
|
||||
avatarModel.trust.postValue(securityLevel)
|
||||
contact.postValue(avatarModel!!)
|
||||
} else {
|
||||
Log.error("$TAG No avatar model found!")
|
||||
}
|
||||
*/
|
||||
|
||||
// When Post Quantum is available, ZRTP is Post Quantum
|
||||
let isZrtpPqTmp = Core.getPostQuantumAvailable
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.isRemoteDeviceTrusted = isRemoteDeviceTrustedTmp
|
||||
self.isMediaEncrypted = true
|
||||
self.isZrtpPq = isZrtpPqTmp
|
||||
|
||||
if isDeviceTrusted {
|
||||
ToastViewModel.shared.toastMessage = "Info_call_securised"
|
||||
ToastViewModel.shared.displayToast = true
|
||||
}
|
||||
}
|
||||
|
||||
if !isDeviceTrusted && authToken != nil && !authToken!.isEmpty {
|
||||
Log.info("[CallViewModel] Showing ZRTP SAS confirmation dialog")
|
||||
self.showZrtpSasDialog(authToken: authToken!)
|
||||
}
|
||||
case MediaEncryption.SRTP, MediaEncryption.DTLS:
|
||||
DispatchQueue.main.async {
|
||||
self.isMediaEncrypted = true
|
||||
self.isZrtpPq = false
|
||||
}
|
||||
default:
|
||||
DispatchQueue.main.async {
|
||||
self.isMediaEncrypted = false
|
||||
self.isZrtpPq = false
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
let securityLevel = isDeviceTrusted ? SecurityLevel.Safe : SecurityLevel.Encrypted
|
||||
let avatarModel = contact
|
||||
if (avatarModel != nil) {
|
||||
avatarModel.trust.postValue(securityLevel)
|
||||
contact.postValue(avatarModel!!)
|
||||
} else {
|
||||
Log.error("$TAG No avatar model found!")
|
||||
}
|
||||
*/
|
||||
|
||||
isMediaEncrypted = true
|
||||
// When Post Quantum is available, ZRTP is Post Quantum
|
||||
isZrtpPq = Core.getPostQuantumAvailable
|
||||
|
||||
if !isDeviceTrusted && authToken != nil && !authToken!.isEmpty {
|
||||
Log.info("[CallViewModel] Showing ZRTP SAS confirmation dialog")
|
||||
showZrtpSasDialog(authToken: authToken!)
|
||||
}
|
||||
|
||||
return isDeviceTrusted
|
||||
case MediaEncryption.SRTP, MediaEncryption.DTLS:
|
||||
isMediaEncrypted = true
|
||||
isZrtpPq = false
|
||||
return false
|
||||
default:
|
||||
isMediaEncrypted = false
|
||||
isZrtpPq = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func showZrtpSasDialogIfPossible() {
|
||||
if currentCall != nil && currentCall!.currentParams != nil && currentCall!.currentParams!.mediaEncryption == MediaEncryption.ZRTP {
|
||||
let authToken = currentCall!.authenticationToken
|
||||
let authToken = currentCall!.localAuthenticationToken
|
||||
let isDeviceTrusted = currentCall!.authenticationTokenVerified && authToken != nil
|
||||
Log.info(
|
||||
"[CallViewModel] Current call media encryption is ZRTP, auth token is \(isDeviceTrusted ? "trusted" : "not trusted yet")"
|
||||
)
|
||||
if (authToken != nil && !authToken!.isEmpty) {
|
||||
if authToken != nil && !authToken!.isEmpty {
|
||||
showZrtpSasDialog(authToken: authToken!)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue