ZRTP Changes

This commit is contained in:
Benoit Martins 2024-06-10 14:55:29 +02:00
parent eca85b80ad
commit 7fb63c19dd
3 changed files with 124 additions and 110 deletions

View file

@ -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 {

View file

@ -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 {

View file

@ -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!)
}
}