@@ -26,9 +26,19 @@ enum HandlePushDeviceRequestError: Error {
2626 case invalidNotificationType
2727}
2828
29+ enum BackgroundNotificationType : String {
30+ case campusTokenRequest = " CAMPUS_TOKEN_REQUEST "
31+ }
32+
33+
34+ /**
35+ Integrates keychain management, including generating and storing of public and private RSA keys.
36+ Handles registering the device id in the backend, responding to background notifications and reading the Campus API Token from the keychain.
37+
38+ The `PushNotification` class can be used as singleton by accessing the static `shared` property.
39+ */
2940class PushNotifications {
30-
31- private static let privateKeyApplicationTag = " de.tum.tca.keys.push_public_key "
41+ private static let privateKeyApplicationTag = " de.tum.tca.keys.push_rsa_key "
3242 private static let keychainAccessGroupName = " 2J3C6P6X3N.de.tum.tca.notificationextension "
3343 private static let keyType = kSecAttrKeyTypeRSA as String
3444 private static let keySize = 2048
@@ -38,6 +48,11 @@ class PushNotifications {
3848
3949 static let shared = PushNotifications ( )
4050
51+ /**
52+ Registers the `deviceToken` in the backend to receive push notifications.
53+
54+ - Parameter deviceToken: the current device token
55+ */
4156 func registerDeviceToken( _ deviceToken: String ) async -> Void {
4257 do {
4358 let keyPair = try getPublicPrivateKeys ( )
@@ -48,7 +63,9 @@ class PushNotifications {
4863 $0. deviceType = . ios
4964 } )
5065
51- let response = try await CampusBackend . shared. registerDevice ( device)
66+ print ( keyPair. publicKey)
67+
68+ let _ = try await CampusBackend . shared. registerDevice ( device)
5269 } catch RSAKeyPairError . failedGeneratingPrivateKey {
5370 print ( " Something went wrong while generating the rsa private key " )
5471 } catch RSAKeyPairError . failedObtainingKeyPairFromKeyChain {
@@ -59,6 +76,16 @@ class PushNotifications {
5976
6077 }
6178
79+ /**
80+ Handles incoming background notification requests from the backend.
81+
82+ - Throws:
83+ - `HandlePushDeviceRequestError.noRequestId`: if the push notification body does not contain the `request_id` parameter
84+ - `HandlePushDeviceRequestError.noNotificationType`: if if the push notification body does not contain the `notification_type` parameter
85+ - `HandlePushDeviceRequestError.invalidNotificationType`: if the `notification_type` is other then `BackgroundNotificationType`
86+
87+ - Parameter data: the background notification body
88+ */
6289 func handleBackgroundNotification( data: [ AnyHashable : Any ] ) async throws {
6390 guard let requestId = data [ " request_id " ] as? String else {
6491 print ( " Failed responding to push device request because no 'request_id' was defined " )
@@ -71,14 +98,21 @@ class PushNotifications {
7198 }
7299
73100 switch notificationType {
74- case " CAMPUS_TOKEN_REQUEST " :
101+ case BackgroundNotificationType . campusTokenRequest . rawValue :
75102 return try await handleCampusTokenRequest ( requestId)
76103 default :
77104 print ( " Failed responding to push device request because 'notification_type' was invalid " )
78105 throw HandlePushDeviceRequestError . invalidNotificationType
79106 }
80107 }
81108
109+ /**
110+ Handles a `BackgroundNotificationType.campusTokenRequest`.
111+ Reads the `campusToken` from the keychain and sends it to the backend including the `requestId`.
112+
113+ - Parameter requestId: identifies the background notification request in the backend and needs to be transmitted with the campus token
114+ - Throws: `HandlePushDeviceRequestError.noCampusToken` if the campus token cannot be read from the keychain
115+ */
82116 private func handleCampusTokenRequest( _ requestId: String ) async throws {
83117 guard let campusToken = self . campusToken else {
84118 print ( " Failed responding to push device request because no campus token was available " )
@@ -90,7 +124,7 @@ class PushNotifications {
90124 $0. requestID = requestId
91125 } )
92126
93- let res = try await CampusBackend . shared. iOSDeviceRequestResponse ( response)
127+ let _ = try await CampusBackend . shared. iOSDeviceRequestResponse ( response)
94128 }
95129
96130 private var campusToken : String ? {
@@ -109,26 +143,38 @@ class PushNotifications {
109143 return try ? PropertyListDecoder ( ) . decode ( Credentials . self, from: data)
110144 }
111145
146+ /**
147+ Checks if the there are already public and private keys stored in the keychain. If yes, it just returns them, otherwise it generates new ones.
148+
149+ - Returns: A tuple containing the RSA public and private key
150+ */
112151 private func getPublicPrivateKeys( ) throws -> RSAKeyPair {
113- if checkIfPrivateKeyAlreadyExists ( ) {
152+ /* if checkIfPrivateKeyAlreadyExists() {
153+ print("Obtaining private key from keychain")
114154 return try obtainPublicPrivateKeyFromKeyChain()
115- }
155+ }*/
116156
117- try generatePrivateKeys ( )
157+ try generatePrivateKey ( )
118158
119159 return try obtainPublicPrivateKeyFromKeyChain ( )
120160 }
121161
162+ /**
163+ Uses `CryptoExportImportManager` to export the public key in a format (PEM) that can be read by the backend
164+ */
122165 private func exportPublicKeyAsValidPEM( _ publicKey: Data ) -> String {
123166 let exportManager = CryptoExportImportManager ( )
124167
125168 return exportManager. exportRSAPublicKeyToPEM ( publicKey, keyType: PushNotifications . keyType, keySize: PushNotifications . keySize)
126169 }
127170
128- private func generatePrivateKeys( ) throws {
171+ /**
172+ Generates a private inside the Keychain can then be queried afterwards
173+ */
174+ private func generatePrivateKey( ) throws {
129175 let attributes : [ String : Any ] = [
130- kSecAttrKeyType as String : kSecAttrKeyTypeEC ,
131- kSecAttrKeySizeInBits as String : 2048 ,
176+ kSecAttrKeyType as String : PushNotifications . keyType ,
177+ kSecAttrKeySizeInBits as String : PushNotifications . keySize ,
132178 kSecPrivateKeyAttrs as String : [
133179 kSecAttrIsPermanent as String : true ,
134180 kSecAttrApplicationTag as String : PushNotifications . privateKeyApplicationTag
@@ -142,6 +188,7 @@ class PushNotifications {
142188 }
143189 }
144190
191+
145192 private func checkIfPrivateKeyAlreadyExists( ) -> Bool {
146193 do {
147194 let _ = try obtainPrivateKeyFromKeyChain ( )
@@ -152,6 +199,9 @@ class PushNotifications {
152199 return true
153200 }
154201
202+ /**
203+ Tries to query for the private key using the `privateKeyKeychainQuery`
204+ */
155205 private func obtainPrivateKeyFromKeyChain( ) throws -> SecKey {
156206 var item : CFTypeRef ?
157207 let status = SecItemCopyMatching ( privateKeyKeychainQuery as CFDictionary , & item)
@@ -163,7 +213,9 @@ class PushNotifications {
163213 return item as! SecKey
164214 }
165215
166-
216+ /**
217+ Obtains the private key from the keychain, creates a public key from the private key and finally returns an external representation for the keys.
218+ */
167219 private func obtainPublicPrivateKeyFromKeyChain( ) throws -> RSAKeyPair {
168220 let privateKey = try obtainPrivateKeyFromKeyChain ( )
169221
0 commit comments