TrioRemoteControl.swift 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import CoreData
  2. import Foundation
  3. import Swinject
  4. class TrioRemoteControl: Injectable {
  5. static let shared = TrioRemoteControl()
  6. @Injected() internal var tempTargetsStorage: TempTargetsStorage!
  7. @Injected() internal var carbsStorage: CarbsStorage!
  8. @Injected() internal var nightscoutManager: NightscoutManager!
  9. @Injected() internal var overrideStorage: OverrideStorage!
  10. @Injected() internal var settings: SettingsManager!
  11. @Injected() internal var iobService: IOBService!
  12. private let timeWindow: TimeInterval = 600
  13. internal let pumpHistoryFetchContext: NSManagedObjectContext
  14. internal let viewContext: NSManagedObjectContext
  15. private init() {
  16. pumpHistoryFetchContext = CoreDataStack.shared.newTaskContext()
  17. viewContext = CoreDataStack.shared.persistentContainer.viewContext
  18. injectServices(TrioApp.resolver)
  19. }
  20. func handleRemoteNotification(encryptedData: String) async throws {
  21. let isTrioRemoteControlEnabled = UserDefaults.standard.bool(forKey: "isTrioRemoteControlEnabled")
  22. guard isTrioRemoteControlEnabled else {
  23. await logError("Remote command received, but remote control is disabled in settings. Ignoring the command.")
  24. return
  25. }
  26. let storedSecret = UserDefaults.standard.string(forKey: "trioRemoteControlSharedSecret") ?? ""
  27. guard !storedSecret.isEmpty else {
  28. await logError("Command rejected: shared secret is missing in settings. Cannot authenticate the command.")
  29. return
  30. }
  31. guard let messenger = SecureMessenger(sharedSecret: storedSecret) else {
  32. await logError("Command rejected: Failed to initialize security module. The shared secret might be invalid.")
  33. return
  34. }
  35. let commandPayload: CommandPayload
  36. do {
  37. commandPayload = try messenger.decrypt(base64EncodedString: encryptedData)
  38. } catch {
  39. await logError(
  40. "Command rejected: Decryption failed. Mismatched shared secret or corrupted message. Error: \(error.localizedDescription)"
  41. )
  42. return
  43. }
  44. let currentTime = Date().timeIntervalSince1970
  45. let timeDifference = currentTime - commandPayload.timestamp
  46. if timeDifference > timeWindow {
  47. await logError(
  48. "Command rejected: the message is too old (sent \(Int(timeDifference)) seconds ago).",
  49. payload: commandPayload
  50. )
  51. return
  52. } else if timeDifference < -timeWindow {
  53. await logError(
  54. "Command rejected: the message has an invalid future timestamp.",
  55. payload: commandPayload
  56. )
  57. return
  58. }
  59. debug(
  60. .remoteControl,
  61. "Command successfully decrypted and authenticated. Time difference: \(Int(timeDifference)) seconds."
  62. )
  63. switch commandPayload.commandType {
  64. case .bolus:
  65. try await handleBolusCommand(commandPayload)
  66. case .tempTarget:
  67. try await handleTempTargetCommand(commandPayload)
  68. case .cancelTempTarget:
  69. await cancelTempTarget(commandPayload)
  70. case .meal:
  71. try await handleMealCommand(commandPayload)
  72. if commandPayload.bolusAmount != nil {
  73. try await handleBolusCommand(commandPayload)
  74. }
  75. case .startOverride:
  76. await handleStartOverrideCommand(commandPayload)
  77. case .cancelOverride:
  78. await handleCancelOverrideCommand(commandPayload)
  79. }
  80. }
  81. }